2010-07-31 29 views
9

Tôi đã viết một lớp với constructor được bảo vệ, để các cá thể mới chỉ có thể được tạo ra với một hàm create() tĩnh trả về shared_ptr cho lớp của tôi. Để cung cấp phân bổ hiệu quả tôi muốn sử dụng boost :: make_shared bên trong hàm create, tuy nhiên trình biên dịch phàn nàn rằng hàm tạo lớp của tôi được bảo vệ bên trong boost :: make_shared. Tôi quyết định tăng cường của tôi :: make_shared một người bạn của lớp học của tôi nhưng tôi đang bối rối về cú pháp. Tôi đã thửLàm thế nào để tăng cường :: make_shared một người bạn của lớp học của tôi

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

nhưng trình biên dịch đã cho tôi lỗi cú pháp. Hãy giúp tôi.

Trả lời

10

Bạn không cần phải template phần friend, nhưng bạn cần phải biểu thị rằng friend chức năng là một mẫu:

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

đó làm việc với Comeau và các phiên bản GCC hiện nhưng không thành công với VC. Tốt hơn sẽ là dạng sau:

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

Điều đó hoạt động trên nhiều trình biên dịch bây giờ - tôi đã thử nghiệm trên VC8, VC10, GCC 4.2, GCC 4.5 và Comeau 4.3.

Hoặc bằng cách sử dụng tên đủ điều kiện để tham chiếu một mẫu cụ thể của mẫu chức năng như Martin nên hoạt động và thực hiện với Comeau, nhưng GCC chokes trên đó.

Một lựa chọn hữu ích mà không phụ thuộc vào các chi tiết thi hành make_shared() (và do đó cũng làm việc với VC10s TR1 implementation) là sử dụng pass-key-idiom cho việc truy cập bảo vệ của các nhà xây dựng và làm bạn với các create() chức năng thay vào đó, ví dụ:

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

Tôi vừa mới đọc bài đăng Sutter Herb về chủ đề và kết quả là không có giải pháp di động cho vấn đề này. Đoạn mã mà bạn đã cung cấp cho hầu hết các trình biên dịch ngoại trừ GCC mà tôi đã phát triển. Vì vậy, thay vì sử dụng make_shared tôi đã hoàn nguyên về việc sử dụng hàm tạo shared_ptr bình thường. – kyku

+0

Bạn có thể vui lòng đăng liên kết tới bài đăng đó không? – Basilevs

+1

@kyk: Phiên bản nào? Tôi đã thử nghiệm trên GCC ... Lưu ý rằng bài viết của Sutters là 7 tuổi. –

2

Tôi sẽ thử mà không cần phần template. Sau khi tất cả, bạn muốn một instantiation cụ thể của (mẫu) chức năng là một người bạn của lớp học của bạn, phải không? Có

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

hoạt động?

Nếu đó không phải là giải pháp, nó có thể hữu ích để cung cấp cho chúng tôi những thông điệp biên dịch bạn đang nhận được ...

+4

Trừ khi đặc tả này có thể hoạt động đôi khi, sau đó ngắt khi thực hiện 'boost :: make_shared' bị thay đổi. Bạn không thể biết. Bạn nên ** không bao giờ ** tuyên bố bạn bè một số mã mà bạn không kiểm soát. – curiousguy

+0

Tôi không thể thấy sự thay đổi trong tương lai trong việc thực hiện 'boost :: make_shared' có thể phá vỡ bất cứ điều gì. Sau khi tất cả khai báo nó như là một 'friend' có cùng hiệu ứng với' make_shared' như là làm cho tất cả các thành viên của lớp của bạn được công khai, nó được bao hàm hoàn toàn bởi đặc tả 'make_shared'. Afaik, 'friend' sẽ không bao giờ thay đổi hành vi thời gian chạy nó chỉ ảnh hưởng đến khả năng hiển thị tại thời gian biên dịch. Tôi đồng ý rằng điều này có lẽ không phải là phong cách tốt và có lẽ nên tránh, nhưng imo đây không phải là một lý do cho một downvote :) (sau khi tất cả, câu trả lời chấp nhận đề xuất chủ yếu là cùng một giải pháp ...) – MartinStettner

+2

"_Sau khi tất cả tuyên bố nó như là một người bạn có cùng tác dụng với make_shared khi làm cho tất cả các thành viên trong lớp của bạn public_ "Tuyệt đối không: nó làm cho họ có thể truy cập được ** định nghĩa ** của' boost :: make_shared', và bạn không biết định nghĩa của ' boost :: make_shared' chứa (và bạn không nên quan tâm). Dù sao, ** nếu một đặc điểm kỹ thuật nói rằng một cái gì đó phải được công khai, hơn nó phải được công khai **, kết thúc của câu chuyện. – curiousguy

2

Tôi nghĩ đó không phải là nơi thích hợp để sử dụng make_shared. Chỉ cần xây dựng đối tượng của bạn với toán tử new và truyền con trỏ tới hàm tạo shared_ptr. Bằng cách đó bạn không cần phải làm bạn với bất kỳ ai.

BTW, tại sao đối số mẫu và đối số chức năng có loại khác nhau?

+3

Vì không có giải pháp di động cho vấn đề tôi chọn câu trả lời này là được chấp nhận. BTW make_shared là preffered vì lý do hiệu suất, bởi vì nó đòi hỏi một cấp phát bộ nhớ cho mỗi shared_ptr và cung cấp cho bộ nhớ cache tốt hơn địa phương. – kyku

-1

Chỉ cần một bản tóm tắt về cách một phiên bản hoàn toàn có thể trông giống như:

#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

Trường hợp có bảo đảm rằng 'tăng bạn bè :: shared_ptr tăng mức lợi ích :: make_shared (const int & x);' có ảnh hưởng gì không? – curiousguy

0

Dưới đây là một số macro tôi đã viết lên để làm điều này cho bạn. Trong trường hợp của bạn, bạn sẽ sử dụng:

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

định nghĩa Macro:

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

tôi đã kết thúc bằng cách sử dụng giải pháp dưới đây đơn giản để thực thi quyền sở hữu chung. Không cần tình bạn.

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)...); 
    } 
}; 
Các vấn đề liên quan