Omn*_*ous 162 c++ shared-ptr c++11
我有这个代码不起作用,但我认为意图很明确:
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();
}
Run Code Online (Sandbox Code Playgroud)
但是我在编译时遇到了这个错误:
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
Run Code Online (Sandbox Code Playgroud)
此消息基本上是说模板实例化堆栈中的某些随机方法向下::std::make_shared无法访问构造函数,因为它受到保护.
但是我真的想要同时使用这两个::std::make_shared并阻止任何人制作这个类没有指向的类的对象::std::shared_ptr.有没有办法实现这个目标?
Omn*_*ous 104
这个答案可能更好,而且我可能会接受.但我也想出了一个更丑陋的方法,但仍然让所有内容仍然是内联的,并且不需要派生类:
#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);
}
Run Code Online (Sandbox Code Playgroud)
编辑2017-01-06:我改变了这一点,明确表示这个想法清楚而简单地可以扩展到带有参数的构造函数,因为其他人在这些方面提供了答案并且似乎对此感到困惑.
Luc*_*ton 75
查看std::make_shared20.7.2.2.6 shared_ptr创建[util.smartptr.shared.create]中的要求,第1段:
要求:表达式
::new (pv) T(std::forward<Args>(args)...),其中pv有类型void*和指向适合于容纳类型对象的存储T,应该很好地形成.A应是分配者(17.6.3.5).复制构造函数和析构函数A不应抛出异常.
由于要求是无条件地根据该表达式指定的,并且不考虑范围之类的东西,我认为友谊这样的技巧是正确的.
一个简单的解决方案是派生自A.这不需要制作A接口甚至是多态类型.
// 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>();
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*ley 67
可能是最简单的解决方案.根据Mohit Aron 先前的回答并结合dlf的建议.
#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() {}
};
Run Code Online (Sandbox Code Playgroud)
小智 25
这是一个很好的解决方案:
#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>();
}
Run Code Online (Sandbox Code Playgroud)
alp*_*pha 11
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_;
};
Run Code Online (Sandbox Code Playgroud)
Sea*_*ean 10
这个怎么样?
static std::shared_ptr<A> create()
{
std::shared_ptr<A> pA(new A());
return pA;
}
Run Code Online (Sandbox Code Playgroud)
我遇到了同样的问题,但现有的答案都不是真正令人满意,因为我需要将参数传递给受保护的构造函数。此外,我需要对几个类执行此操作,每个类都采用不同的参数。
为此,在几个都使用类似方法的现有答案的基础上,我提出了这个小金点:
template < typename Object, typename... Args >
inline std::shared_ptr< Object >
protected_make_shared( Args&&... args )
{
struct helper : public Object
{
helper( Args&&... args )
: Object{ std::forward< Args >( args )... }
{}
};
return std::make_shared< helper >( std::forward< Args >( args )... );
}
Run Code Online (Sandbox Code Playgroud)
由于我不喜欢已提供的答案,我决定搜索并找到一个不像以前的答案那样通用的解决方案,但我更喜欢它(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
该解决方案的作用是将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;
}
Run Code Online (Sandbox Code Playgroud)
一个更复杂的例子,它基于我正在研究的实用程序.有了这个,我无法使用Luc的解决方案.但Omnifarius的那个可以改编.不是说在前面的例子中,每个人都可以使用MyAlloc创建一个A对象,除了create()方法之外,没有办法创建A或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;
}
Run Code Online (Sandbox Code Playgroud)
理想情况下,我认为完美的解决方案需要对 C++ 标准进行补充。Andrew Schepler 提出以下建议:
(去这里查看整个线程)
我们可以从 boost::iterator_core_access 中借用一个想法。我提出一个
std::shared_ptr_access没有公共或受保护成员的新类,并为 std::make_shared(args...) 和 std::alloc_shared(a, args...) 指定表达式 ::new(pv) T (forward(args)...) 和 ptr->~T() 在 std::shared_ptr_access 的上下文中必须是良构的。std::shared_ptr_access 的实现可能如下所示:
namespace std {
class shared_ptr_access
{
template <typename _T, typename ... _Args>
static _T* __construct(void* __pv, _Args&& ... __args)
{ return ::new(__pv) _T(forward<_Args>(__args)...); }
template <typename _T>
static void __destroy(_T* __ptr) { __ptr->~_T(); }
template <typename _T, typename _A>
friend class __shared_ptr_storage;
};
}
Run Code Online (Sandbox Code Playgroud)
如果/当将上述内容添加到标准中时,我们只需执行以下操作:
class A {
public:
static std::shared_ptr<A> create() {
return std::make_shared<A>();
}
protected:
friend class std::shared_ptr_access;
A() {}
A(const A &) = delete;
const A &operator =(const A &) = delete;
};
Run Code Online (Sandbox Code Playgroud)
如果这对您来说也是标准的重要补充,请随时将您的 2 美分添加到链接的 isocpp Google 群组中。
当你有两个严格相关的类 A 和 B 一起工作时,就会出现一个更棘手、更有趣的问题。
说A是“主人阶级”,B是“奴隶”。如果你想限制 B 的实例化仅限于 A,你可以将 B 的构造函数设为私有,并将 B 设为 A 的友元,如下所示
class B
{
public:
// B your methods...
private:
B();
friend class A;
};
Run Code Online (Sandbox Code Playgroud)
std::make_shared<B>()不幸的是,从 的方法调用A会使编译器抱怨它B::B()是私有的。
我的解决方案是在内部创建一个公共Pass虚拟类(就像nullptr_t)B,它具有私有构造函数并且是朋友,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);
}
};
Run Code Online (Sandbox Code Playgroud)