【问题标题】:How to make boost::make_shared a friend of my class如何让 boost::make_shared 成为我班的朋友
【发布时间】:2011-03-23 15:41:00
【问题描述】:

我编写了一个带有受保护构造函数的类,因此只能使用静态 create() 函数生成新实例,该函数将 shared_ptr's 返回给我的类。为了提供有效的分配,我想在 create 函数中使用 boost::make_shared,但是编译器抱怨我的类构造函数在 boost::make_shared 中受到保护。我决定将我的 boost::make_shared 分享给我班上的一个朋友,但我对语法感到困惑。我试过了

template< class T, class A1, class A2 >
friend boost::shared_ptr<Connection> boost::make_shared(const ConnectionManagerPtr&, const std::string&);

但是编译器给了我语法错误。请帮忙。

【问题讨论】:

  • 截至 2020 年 9 月和 Boost 1.74,仍然没有支持和安全的方式来与 boost::make_shared 建立友谊。

标签: c++ templates boost friend make-shared


【解决方案1】:

您不需要对friend 部分进行模板化,但您需要表明friend 函数是一个模板:

friend boost::shared_ptr<Connection> boost::make_shared<>(/* ... */);
//                                                     ^^

这适用于 Comeau 和当前的 GCC 版本,但不适用于 VC。最好是以下形式:

friend boost::shared_ptr<Connection> boost::make_shared<Connection>(/* ... */);

现在可以跨多个编译器工作 - 我在 VC8、VC10、GCC 4.2、GCC 4.5 和 Comeau 4.3 上对其进行了测试。

或者使用限定名称来引用函数模板的特定实例,就像 Martin 应该对 Comeau 所做的那样,但 GCC 对此感到窒息。

不依赖于make_shared() 的实现细节(因此也适用于VC10s TR1 implementation)的一个有用的替代方法是使用pass-key-idiom 来保护构造函数并与create() 成为朋友代替函数,例如:

class Connection {
// ...
public:
    class Key {
        friend boost::shared_ptr<Connection> create(const ConnectionManagerPtr&, 
                                                    const std::string&);
        Key() {}
    };
    Connection(const ConnectionManagerPtr&, const std::string&, const Key&);
};

boost::shared_ptr<Connection> create(const ConnectionManagerPtr& p, 
                                     const std::string& s) 
{
    return boost::make_shared<Connection>(p, s, Connection::Key());
}

【讨论】:

  • 我刚刚阅读了 Herb Sutter 关于该主题的帖子,结果是没有针对此问题的便携式解决方案。您提供的代码 sn-p 适用于几乎所有编译器,除了我碰巧开发的 GCC。因此,我没有使用 make_shared,而是使用普通的 shared_ptr 构造函数。
  • 能否请您发布该帖子的链接?
  • @kyk:什么版本?我在 GCC 上对此进行了测试...请注意,萨特斯的文章已有 7 年历史。
  • 好的,我遇到的问题是我在朋友声明中跳过了构造函数参数类型。所以它应该类似于朋友 boost::shared_ptr boost::make_shared(const std::string&);
【解决方案2】:

我会尝试不使用 template 部分。毕竟,您希望(模板)函数的特定实例化成为您班级的朋友,不是吗?有没有

friend boost::shared_ptr<Connection> boost::make_shared(const ConnectionManagerPtr&, const std::string&);

工作?

如果这不是解决方案,向我们提供您收到的编译器消息可能会有所帮助...

【讨论】:

  • 除非规范boost::make_shared 说这会起作用,它有时可能会起作用,然后在boost::make_shared 的实现发生更改时中断。你无法知道。您应该永远将一些您无法控制的代码声明为朋友。
  • 我看不出boost::make_shared 实现的未来变化会如何破坏任何东西。毕竟,将其声明为friendmake_shared 具有相同的效果,就像使您的类的所有成员都公开一样,make_shared 的规范完全涵盖了这一点。 Afaik,friend 永远不会改变运行时行为,它只会影响编译时的可见性。我同意这可能不是好的风格,也许应该避免,但我认为这不是投反对票的理由:)(毕竟,接受的答案主要提出了相同的解决方案......)
  • "毕竟将其声明为朋友对 make_shared 的效果与将您班级的所有成员公开" 绝对不是:它使 可以访问它们boost::make_shared 的定义,你不知道boost::make_shared 的定义包含什么(你不应该关心)。无论如何,如果规范说某些东西必须是公开的,那么它必须是公开的,故事结束。
【解决方案3】:

我认为这不是使用 make_shared 的正确位置。只需使用 operator new 构造您的对象并将指针传递给 shared_ptr 构造函数。这样你就不需要和任何人成为朋友了。

顺便说一句,为什么模板参数和函数参数的类型不同?

【讨论】:

  • 由于该问题没有可移植的解决方案,因此我选择此答案作为已接受。 BTW make_shared 出于性能原因是首选,因为它需要为每个 shared_ptr 分配一个内存并提供更好的缓存局部性。
【解决方案4】:

我最终使用了以下简单的解决方案来强制共享所有权。不需要友谊。

class probe {
    probe() = default;
    probe(...) { ... }

    // Part I of III, private
    struct creation_token {};
    probe(probe const&) = delete;
    probe& operator=(probe const&) = delete;

public:
    // Part II of III, public
    template <class... Args>
    probe(creation_token&&, Args&&... args):
        probe(std::forward<Args>(args)...) {}

    // Part III of III, public
    template <class... Args>
    static auto create(Args&&... args) {
        return make_shared<probe>(creation_token(),
            std::forward<Args>(args)...);
    }
};

【讨论】:

    【解决方案5】:

    以下是我为您编写的一些宏。在您的情况下,您将使用:

    BOOST_MAKE_SHARED_2ARG_CONSTRUCTOR(Connection, const ConnectionManagerPtr&, const std::string&);
    

    宏定义:

    // Required includes
    #include <boost/make_shared.hpp>
    #include <boost/type_traits/add_reference.hpp>
    #include <boost/type_traits/add_const.hpp> 
    
    // Helper macro
    #define CONST_REFERENCE(T) boost::add_reference<boost::add_const<T>::type>::type
    
    /** BOOST_MAKE_SHARED_nARG_CONSTRUCTOR(CLASS_NAME, ARG1_TYPE, ARG2_TYPE, ...) 
      *
      * Use this macro inside the body of a class to declare that boost::make_shared
      * should be considered a friend function when used in conjunction with the
      * constructor that takes the given argument types.  This allows the constructor 
      * to be declared private (making it impossible to accidentally create an instance 
      * of the object without immediatly storing it in a boost::shared_ptr).  
      * Example usage:
      *
      * class Foo {
      *   private:
      *     Foo(int size, const char* name);
      *     MAKE_SHARED_2ARG_CONSTRUCTOR(Foo, int, const char*);
      * };
      * 
      * boost::shared_ptr<Foo> myFoo = boost::make_shared<Foo>(3, "Bob");
      *
      * Note that you need to explicitly specify the number of arguments 
      * that the constructor takes as part of the macro name.  Also, note that 
      * macros don't mix well with templated types that contain commas -- so 
      * if you have such a type, then you should typedef it to a shorter name 
      * before using it with this macro.
      */
    #define BOOST_MAKE_SHARED_0ARG_CONSTRUCTOR(CLASS_NAME) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>()
    #define BOOST_MAKE_SHARED_1ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1))
    #define BOOST_MAKE_SHARED_2ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2))
    #define BOOST_MAKE_SHARED_3ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3))
    #define BOOST_MAKE_SHARED_4ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4))
    #define BOOST_MAKE_SHARED_5ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5))
    #define BOOST_MAKE_SHARED_6ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5, ARG_TYPE6) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5), CONST_REFERENCE(ARG_TYPE6))
    #define BOOST_MAKE_SHARED_7ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5, ARG_TYPE6, ARG_TYPE7) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5), CONST_REFERENCE(ARG_TYPE6)), CONST_REFERENCE(ARG_TYPE7))
    #define BOOST_MAKE_SHARED_8ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5, ARG_TYPE6, ARG_TYPE7, ARG_TYPE8) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5), CONST_REFERENCE(ARG_TYPE6)), CONST_REFERENCE(ARG_TYPE7)), CONST_REFERENCE(ARG_TYPE8))
    #define BOOST_MAKE_SHARED_9ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5, ARG_TYPE6, ARG_TYPE7, ARG_TYPE8, ARG_TYPE9) \
        friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5), CONST_REFERENCE(ARG_TYPE6)), CONST_REFERENCE(ARG_TYPE7)), CONST_REFERENCE(ARG_TYPE8)), CONST_REFERENCE(ARG_TYPE9))
    

    【讨论】:

      【解决方案6】:

      只是一个完整版本的摘要:

      #include <iostream>
      #include <boost/make_shared.hpp>
      
      class Foo {
        explicit Foo(int x) {
          std::cout << "Foo::Foo(" << x << ")\n";
        }
      public:
        friend boost::shared_ptr<Foo> boost::make_shared<Foo, int>(const int& x);
      
        static boost::shared_ptr<Foo> create(int x) {
          return boost::make_shared<Foo, int>(x);
        }
      
        ~Foo() {
          std::cout << "Foo::~Foo()\n";
        }
      };
      
      int main(int argc, const char *argv[]) {
        Foo::create(42);
      }
      

      【讨论】:

      • 哪里有保证friend boost::shared_ptr&lt;Foo&gt; boost::make_shared&lt;Foo, int&gt;(const int&amp; x);有效果?
      • 如果这仍然有效,我可能会支持它;但是(A)它在 C++11 中被破坏了,因为int&amp;&amp;,并且(B)无论如何都对boost::make_shared&lt;Foo, int&gt;(x) 投了反对票。永远不应使用不必要的显式模板参数调用函数模板。只是boost::make_shared&lt;Foo&gt;(x) 会更准确。
      猜你喜欢
      • 2016-02-27
      • 1970-01-01
      • 1970-01-01
      • 2012-01-27
      • 1970-01-01
      • 2021-03-21
      • 1970-01-01
      • 1970-01-01
      • 2013-06-07
      相关资源
      最近更新 更多