如何在只有protected或private构造函数的类上调用:: std :: make_shared?
我有这个代码不起作用,但我认为这个意图很明显:
testmakeshared.cpp
#include <memory> class A { public: static ::std::shared_ptr<A> create() { return ::std::make_shared<A>(); } protected: A() {} A(const A &) = delete; const A &operator =(const A &) = delete; }; ::std::shared_ptr<A> foo() { return A::create(); }
但是当我编译它时,我得到这个错误:
g++ -std=c++0x -march=native -mtune=native -O3 -Wall testmakeshared.cpp In file included from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:52:0, from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/memory:86, from testmakeshared.cpp:1: testmakeshared.cpp: In constructor 'std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc) [with _Tp = A, _Alloc = std::allocator<A>, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]': /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:518:8: instantiated from 'std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]' /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:986:35: instantiated from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]' /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:313:64: instantiated from 'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A]' /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:531:39: instantiated from 'std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}]' /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:547:42: instantiated from 'std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = A, _Args = {}]' testmakeshared.cpp:6:40: instantiated from here testmakeshared.cpp:10:8: error: 'A::A()' is protected /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:400:2: error: within this context Compilation exited abnormally with code 1 at Tue Nov 15 07:32:58
这个消息基本上是说从::std::make_shared
模板实例化栈中的一些随机方法不能访问构造函数,因为它是受保护的。
但我真的想同时使用::std::make_shared
和防止任何人做一个对象,这个对象不是由::std::shared_ptr
指向的。 有什么办法可以做到这一点?
这个答案可能会更好,我可能会接受的。 但是我也想出了一个更丑陋的方法,但仍然让所有内容仍然是内联的,并且不需要派生类:
#include <memory> #include <string> class A { protected: struct this_is_private; public: explicit A(const this_is_private &) {} A(const this_is_private &, ::std::string, int) {} template <typename... T> static ::std::shared_ptr<A> create(T &&...args) { return ::std::make_shared<A>(this_is_private{0}, ::std::forward<T>(args)...); } protected: struct this_is_private { explicit this_is_private(int) {} }; A(const A &) = delete; const A &operator =(const A &) = delete; }; ::std::shared_ptr<A> foo() { return A::create(); } ::std::shared_ptr<A> bar() { return A::create("George", 5); } ::std::shared_ptr<A> errors() { ::std::shared_ptr<A> retval; // Each of these assignments to retval properly generates errors. retval = A::create("George"); retval = new A(A::this_is_private{0}); return ::std::move(retval); }
编辑2017-01-06:我改变了这一点,以清楚地表明,这个想法是清楚,简单地扩展到build设者,采取论据,因为其他人正在提供答案沿线,似乎混淆了这一点。
查看20.7.2.2.6 shared_ptr creation [util.smartptr.shared.create]中的std::make_shared
的要求,第1段:
需要:expression式
::new (pv) T(std::forward<Args>(args)...)
,其中pv
types为void*
并指向适合容纳T
types对象的存储区,应该格式良好。A
应该是一个分配器(17.6.3.5)。 A的拷贝构造函数和析构函数不得抛出exception。
既然这个要求是无条件地用这个expression式来规定的,而且没有考虑范围之类的东西,我觉得像友谊这样的技巧是对的。
一个简单的解决scheme是从A
派生。 这不需要把A
作为一个接口,甚至是一个多态的types。
// interface in header std::shared_ptr<A> make_a(); // implementation in source namespace { struct concrete_A: public A {}; } // namespace std::shared_ptr<A> make_a() { return std::make_shared<concrete_A>(); }
可能是最简单的解决scheme。 根据Mohit Aron先前的回答 ,并纳入dlf的build议。
#include <memory> class A { public: static std::shared_ptr<A> create() { struct make_shared_enabler : public A {}; return std::make_shared<make_shared_enabler>(); } private: A() {} };
这是一个很好的解决scheme:
#include <memory> class A { public: static shared_ptr<A> Create(); private: A() {} struct MakeSharedEnabler; }; struct A::MakeSharedEnabler : public A { MakeSharedEnabler() : A() { } }; shared_ptr<A> A::Create() { return make_shared<MakeSharedEnabler>(); }
由于我不喜欢已经提供的答案,所以我决定search并find一个不像以前的答案那样通用的解决scheme,但我更喜欢它(tm)。 回想起来,它并不比Omnifarius提供的更好,但也可能有其他人喜欢它:)
这不是我发明的,而是Jonathan Wakely(GCC开发者)的想法。
不幸的是,它不适用于所有的编译器,因为它依赖于std :: allocate_shared实现中的一个小改动。 但是这个改变现在是标准库的更新,因此将来可能会得到所有编译器的支持。 它适用于GCC 4.7。
C ++标准库工作组更改请求位于: http : //lwg.github.com/issues/lwg-active.html#2070
GCC补丁有一个示例用法: http : //old.nabble.com/Re%3A–v3–Implement-pointer_traits-and-allocator_traits-p31723738.html
该解决scheme的工作原理是使用std :: allocate_shared(而不是std :: make_shared)与一个自定义的分配器,这是与私有构造函数声明的类的朋友。
来自OP的例子看起来像这样:
#include <memory> template<typename Private> struct MyAlloc : std::allocator<Private> { void construct(void* p) { ::new(p) Private(); } }; class A { public: static ::std::shared_ptr<A> create() { return ::std::allocate_shared<A>(MyAlloc<A>()); } protected: A() {} A(const A &) = delete; const A &operator =(const A &) = delete; friend struct MyAlloc<A>; }; int main() { auto p = A::create(); return 0; }
一个更复杂的例子,基于我正在工作的实用程序。 有了这个我不能使用吕克的解决scheme。 但是Omnifarius的那个可以改编。 并不是说在前面的例子中,每个人都可以使用这个MyAlloc创build一个A对象,除了create()方法之外,没有办法创buildA或B.
#include <memory> template<typename T> class safe_enable_shared_from_this : public std::enable_shared_from_this<T> { public: template<typename... _Args> static ::std::shared_ptr<T> create(_Args&&... p_args) { return ::std::allocate_shared<T>(Alloc(), std::forward<_Args>(p_args)...); } protected: struct Alloc : std::allocator<T> { template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } }; safe_enable_shared_from_this(const safe_enable_shared_from_this&) = delete; safe_enable_shared_from_this& operator=(const safe_enable_shared_from_this&) = delete; }; class A : public safe_enable_shared_from_this<A> { private: A() {} friend struct safe_enable_shared_from_this<A>::Alloc; }; class B : public safe_enable_shared_from_this<B> { private: B(int v) {} friend struct safe_enable_shared_from_this<B>::Alloc; }; int main() { auto a = A::create(); auto b = B::create(5); return 0; }
struct A { public: template<typename ...Arg> std::shared_ptr<A> static create(Arg&&...arg) { struct EnableMakeShared : public A { EnableMakeShared(Arg&&...arg) :A(std::forward<Arg>(arg)...) {} }; return std::make_shared<EnableMakeShared>(std::forward<Arg>(arg)...); } void dump() const { std::cout << a_ << std::endl; } private: A(int a) : a_(a) {} A(int i, int j) : a_(i + j) {} A(std::string const& a) : a_(a.size()) {} int a_; };
这个怎么样?
static std::shared_ptr<A> create() { std::shared_ptr<A> pA(new A()); return pA; }
如果你还想启用一个带参数的构造函数,这可能会有所帮助。
#include <memory> #include <utility> template<typename S> struct enable_make : public S { template<typename... T> enable_make(T&&... t) : S(std::forward<T>(t)...) { } }; class foo { public: static std::unique_ptr<foo> create(std::unique_ptr<int> u, char const* s) { return std::make_unique<enable_make<foo>>(std::move(u), s); } protected: foo(std::unique_ptr<int> u, char const* s) { } }; void test() { auto fp = foo::create(std::make_unique<int>(3), "asdf"); }
当你有两个严格相关的类A和B一起工作时,会发生一个更多毛发和有趣的问题。
说A是“大师级”,B是它的“奴隶”。 如果你想限制B的实例化到A,你可以使B的构造函数是私有的,而朋友B是这样的
class B { public: // B your methods... private: B(); friend class A; };
不幸的是,从A
的方法调用std::make_shared<B>()
会使编译器抱怨B::B()
是私有的。
我的解决scheme是在B
中创build一个公开的Pass
伪类(就像nullptr_t
),它具有私有构造函数,并且是A
朋友,并使B
的构造函数公开,并将Pass
添加到它的参数中,像这样。
class B { public: class Pass { Pass() {} friend class A; }; B(Pass, int someArgument) { } }; class A { public: A() { // This is valid auto ptr = std::make_shared<B>(B::Pass(), 42); } }; class C { public: C() { // This is not auto ptr = std::make_shared<B>(B::Pass(), 42); } };
我意识到这个线程是相当老的,但我find了一个答案,不需要inheritance或额外的构造函数的参数,我不能看到其他地方。 这不是便携式:
#include <memory> #if defined(__cplusplus) && __cplusplus >= 201103L #define ALLOW_MAKE_SHARED(x) friend void __gnu_cxx::new_allocator<test>::construct<test>(test*); #elif defined(_WIN32) || defined(WIN32) #if defined(_MSC_VER) && _MSC_VER >= 1800 #define ALLOW_MAKE_SHARED(x) friend class std::_Ref_count_obj; #else #error msc version does not suport c++11 #endif #else #error implement for platform #endif class test { test() {} ALLOW_MAKE_SHARED(test); public: static std::shared_ptr<test> create() { return std::make_shared<test>(); } }; int main() { std::shared_ptr<test> t(test::create()); }
我在Windows和Linux上testing过,可能需要调整不同的平台。
我已经想出了这种模板,很好地克服了受保护构造函数的问题:
// helper template not used directly in code: template<typename T> struct CHelpExposeProtectedConstructor : public T { template<typename...Args> CHelpExposeProtectedConstructor(Args...args) : T(std::forward<Args>(args)...) {} }; /** * this uses std::make_shared on classes which have protected constructors */ template<typename T, typename...Args> auto MakeSharedFromProtected(Args...args) -> std::shared_ptr<T> { return std::make_shared<CHelpExposeProtectedConstructor<T>>(std::forward<Args>(args)...); }
比只使用MakeSharedFromProtected
而不是make_shared
和问题解决了。
可悲的是,这不利于私人build设者。
#include <iostream> #include <memory> class A : public std::enable_shared_from_this<A> { private: A(){} explicit A(int a):m_a(a){} public: template <typename... Args> static std::shared_ptr<A> create(Args &&... args) { class make_shared_enabler : public A { public: make_shared_enabler(Args &&... args):A(std::forward<Args>(args)...){} }; return std::make_shared<make_shared_enabler>(std::forward<Args>(args)...); } int val() const { return m_a; } private: int m_a=0; }; int main(int, char **) { std::shared_ptr<A> a0=A::create(); std::shared_ptr<A> a1=A::create(10); std::cout << a0->val() << " " << a1->val() << std::endl; return 0; }
你可以使用这个:
class CVal { friend std::shared_ptr<CVal>; friend std::_Ref_count<CVal>; public: static shared_ptr<CVal> create() { shared_ptr<CVal> ret_sCVal(new CVal()); return ret_sCVal; } protected: CVal() {}; ~CVal() {}; };