2010-07-31 96 views
9

我寫了一個帶有受保護構造函數的類,以便只能使用靜態create()函數生成新實例,該函數會將shared_ptr返回給我的類。爲了提供有效的分配,我想在create函數中使用boost :: make_shared,但是編譯器會抱怨我的類構造函數在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&); 

但編譯器給了我語法錯誤。請幫忙。

回答

10

您不需要模板的friend一部分,但你需要表示該friend功能是一個模板:

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

與科莫和當前版本的GCC工作,但失敗,VC。更好的將是以下形式:

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

跨多個編譯器現在工作 - 我測試VC8,VC10,GCC 4.2,GCC 4.5和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()); 
} 
+0

我剛剛閱讀了Herb Sutter的文章,結果是沒有便攜式解決方案來解決這個問題。您給出的代碼片段幾乎可以在除了GCC之外的所有編譯器上進行工作,這些編譯器都是我正在開發的。因此,我沒有使用make_shared,而是恢復使用普通的shared_ptr構造函數。 – kyku 2010-08-01 11:07:52

+0

您能否發佈該帖子的鏈接? – Basilevs 2010-08-01 14:29:00

+1

@kyk:什麼版本?我在GCC上測試過......注意Sutters的文章已經7歲了。 – 2010-08-01 23:41:13

2

我會嘗試沒有template部分。畢竟,你需要(模板)函數的特定實例作爲你班級的朋友,不是嗎?

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

工作嗎?

如果這不是解決方案,它可能會有所幫助,給我們你得到編譯器的消息...

+4

除非'boost :: make_shared'這個規範說這可以工作,它可能有時會工作,然後當'boost :: make_shared'的實現發生改變時會中斷。你無法知道。你應該**從不**向朋友聲明一些你無法控制的代碼。 – curiousguy 2011-10-10 13:46:33

+0

我看不到'boost :: make_shared'實現中的未來變化會如何破壞任何東西。畢竟,將它聲明爲'friend'對'make_shared'有同樣的效果,因爲它使得你的類的所有成員都是公共的,這完全被'make_shared'的規範所覆蓋。 Afaik,'friend'永遠不會改變運行時行爲,它只會影響編譯時的可見性。我同意這可能不是好的風格,應該也許應該避免,但是這不是一個downvote的原因:)(畢竟,接受的答案主要提出了相同的解決方案...) – MartinStettner 2011-10-10 15:12:49

+2

「_所有聲明之後一個朋友對make_shared具有與使你的類的所有成員相同的效果public_「絕對不是:它使得它們可以被'boost :: make_shared'的**定義**訪問,並且你不知道' boost :: make_shared'包含(你不應該在乎)。無論如何,**如果一個規範說某事必須是公開的,而不是必須是公開**,故事結束。 – curiousguy 2011-10-10 15:38:09

2

我認爲這是不使用make_shared正確的地方。只需用operator new構造你的對象,並將指針傳遞給shared_ptr構造函數即可。這樣你就不需要和任何人成爲朋友。

順便說一句,爲什麼模板參數和函數參數是不同類型的?

+3

由於沒有便攜式解決方案,我選擇接受的答案。順便說一句make_shared是出於性能方面的原因,因爲它需要每個shared_ptr分配一次內存,並提供更好的緩存局部性。 – kyku 2010-08-01 11:09:49

-1

的完整版本可怎麼看起來就像一個總結:

#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); 
} 
+0

哪裏有保證'朋友提升:: shared_ptr boost :: make_shared (const int & x);'有什麼影響? – curiousguy 2011-10-10 14:11:04

0

下面是一些宏我寫了爲你做到這一點。在你的情況,你可以使用:

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)) 
1

我結束了使用下面的簡單的解決方案,以加強共享所有權。沒有友誼需要。

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)...); 
    } 
}; 
相關問題