2010-07-31 10 views
9

Ich habe eine Klasse mit geschützten Konstruktor geschrieben, so dass neue Instanzen nur mit einer statischen create() -Funktion erzeugt werden können, die shared_ptrs zu meiner Klasse zurückgibt. Um eine effiziente Zuweisung zu ermöglichen, möchte ich boost :: make_shared innerhalb der create-Funktion verwenden, der Compiler beschwert sich jedoch, dass mein Klassenkonstruktor in boost :: make_shared geschützt ist. Ich entschied mich zu meinem boost :: make_shared einen Freund meiner Klasse, aber ich bin verwirrt über die Syntax. Ich versuchteWie boost :: make_shared ein Freund meiner Klasse

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

, aber der Compiler gab mir Syntaxfehler. Bitte helfen Sie.

Antwort

10

Sie brauchen nicht den friend Teil an Template, aber Sie müssen, um anzuzeigen, dass die friend Funktion ist eine Vorlage:

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

, die mit Comeau und aktuellen GCC-Versionen funktionieren aber nicht mit VC. Besser wäre die folgende Form:

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

, die jetzt über mehrere Compiler arbeitet - Getestet habe ich es auf VC8, VC10, GCC 4.2, GCC 4.5 und Comeau 4.3.

Alternativ mit einem qualifizierten Namen auf eine bestimmte Instanz der Funktionsvorlage verweisen, wie Martin sollte arbeiten und macht mit Comeau, aber GCC Drosseln darauf.

Eine nützliche Alternative, die nicht auf die Details der Implementierung abhängig ist von make_shared() (und damit funktioniert auch mit VC10s TR1 implementation) ist die pass-key-idiom für zugriffs Schutz des Konstrukteurs zu verwenden und die create() Funktion stattdessen zB anfreunden:

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

Ich habe gerade Herb Sutter Post zu dem Thema gelesen und das Ergebnis ist, dass es keine portable Lösung für dieses Problem gibt. Das Code-Snippet, das Sie angegeben haben, funktioniert bei fast allen Compilern außer GCC, auf dem ich gerade arbeite. Anstatt also make_shared zu verwenden, benutzte ich den normalen shared_ptr-Konstruktor. – kyku

+0

Könnten Sie bitte einen Link zu diesem Beitrag posten? – Basilevs

+1

@ kyk: Welche Versionen? Ich habe das auf GCC getestet ... Beachten Sie, dass Sutters Artikel 7 Jahre alt ist. –

2

Ich würde ohne den template Teil versuchen. Schließlich möchten Sie eine spezifische Instanziierung der (Template-) Funktion als Freund Ihrer Klasse, nicht wahr? Ist

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

arbeiten?

Wenn das nicht die Lösung, könnte es hilfreich sein, uns die Compiler-Nachrichten geben Sie bekommen ...

+4

Es sei denn, die Spezifikation 'boost :: make_shared' dies sagt funktioniert, könnte es manchmal funktioniert, dann brechen, wenn die Umsetzung der' boost :: make_shared' geändert wird. Du kannst es nicht wissen. Du solltest ** niemals einen Code deklarieren, den du nicht kontrollierst. – curiousguy

+0

Ich kann nicht sehen, wie eine zukünftige Änderung in der Implementierung von 'boost :: make_shared' irgendetwas kaputt machen könnte. Nachdem man es als "Freund" deklariert hat, hat es den gleichen Effekt wie "make_shared" alle Mitglieder einer Klasse öffentlich zu machen, was durch die Spezifikation von 'make_shared' perfekt abgedeckt wird. Afaik, 'Freund' wird niemals das Laufzeitverhalten ändern, es beeinflusst nur die Sichtbarkeit zur Kompilierzeit. Ich stimme zu, dass dies ist wahrscheinlich nicht gut Stil und sollte vielleicht vermieden werden, aber das ist nicht ein Grund für einen Downvote :) (schließlich die vorgeschlagene Antwort schlägt vor allem die gleiche Lösung ...) – MartinStettner

+2

"_Nach allen deklariert es als Ein Freund hat den gleichen Effekt wie make_shared, indem er alle Mitglieder deiner Klasse public_ macht. "Absolut nicht: es macht sie zugänglich für die ** Definition ** von' boost :: make_shared', und du weißt nicht, was die Definition von 'ist boost :: make_shared enthält (und das sollte dir egal sein). Wie auch immer, ** wenn eine Spezifikation sagt, dass etwas öffentlich sein muss, dann muss es öffentlich sein. **, Ende der Geschichte. – curiousguy

2

Ich denke, dass nicht der richtige Ort ist make_shared zu verwenden. Konstruieren Sie Ihr Objekt einfach mit dem Operator new und übergeben Sie den Zeiger an den shared_ptr-Konstruktor. Auf diese Weise musst du mit niemandem befreundet sein.

BTW, warum Template-Argumente und Funktionsargumente von unterschiedlicher Art sind?

+3

Da es keine tragbare Lösung für das Problem gibt, wähle ich diese Antwort als akzeptiert. BTW make_shared wird aus Performance-Gründen bevorzugt, da es pro shared_ptr eine Speicherzuweisung benötigt und eine bessere Cache-Lokalität ergibt. – kyku

-1

Nur eine Zusammenfassung, wie eine vollständige Version kann wie folgt aussehen:

#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

Wo ist die Garantie, dass 'friend boost :: shared_ptr boost :: make_shared (const int & x);' hat irgendeinen Effekt? – curiousguy

0

Im Folgenden sind einige Makros ich schrieb bis dies für Sie tun. In Ihrem Fall würden Sie verwenden:

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

Makrodefinitionen:

// 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

Ich landete die unten einfache Lösung mit gemeinsamer Trägerschaft zu erzwingen. Keine Freundschaft erforderlich.

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)...); 
    } 
}; 
Verwandte Themen