I have this code that doesn't work, but I think the intent is clear:
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();
}
But I get this error when I compile it:
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
This message is basically saying that some random method way down in the template instantiation stack from ::std::make_shared
can't access the constructor because it's protected.
But I really want to use both ::std::make_shared
and prevent anybody from making an object of this class that isn't pointed at by a ::std::shared_ptr
. Is there any way to accomplish this?
Looking at the requirements for
std::make_shared
in 20.7.2.2.6 shared_ptr creation [util.smartptr.shared.create], paragraph 1:Since the requirement is unconditionally specified in terms of that expression and things like scope aren't taken into account, I think tricks like friendship are right out.
A simple solution is to derive from
A
. This needn't require makingA
an interface or even a polymorphic type.Since I didn't like the already provided answers I decided to search on and found a solution that is not as generic as the previous answers but I like it better(tm). In retrospect it is not much nicer than the one provided by Omnifarius but there could be other people who like it too :)
This is not invented by me, but it is the idea of Jonathan Wakely (GCC developer).
Unfortunately it does not work with all the compilers because it relies on a small change in std::allocate_shared implementation. But this change is now a proposed update for the standard libraries, so it might get supported by all the compilers in the future. It works on GCC 4.7.
C++ standard Library Working Group change request is here: http://lwg.github.com/issues/lwg-active.html#2070
The GCC patch with an example usage is here: http://old.nabble.com/Re%3A--v3--Implement-pointer_traits-and-allocator_traits-p31723738.html
The solution works on the idea to use std::allocate_shared (instead of std::make_shared) with a custom allocator that is declared friend to the class with the private constructor.
The example from the OP would look like this:
A more complex example that is based on the utility I'm working on. With this I could not use Luc's solution. But the one by Omnifarius could be adapted. Not that while in the previous example everybody can create an A object using the MyAlloc in this one there is not way to create A or B besides the create() method.
The root of the problem is that if the function or class you friend makes lower level calls to your constructor, they have to be friended too. std::make_shared isn't the function that's actually calling your constructor so friending it makes no difference.
std::_Ref_count_obj is actually calling your constructor, so it needs to be a friend. Since that's a bit obscure, I use a macro
Then your class declaration looks fairly simple. You can make a single macro for declaring the ptr and the class if you prefer.
This is actually an important issue. To make maintainable, portable code you need to hide as much of the implementation as possible.
hides how you're handling your smart pointer a bit, you have to be sure to use your typedef. But if you always have to create one using make_shared, it defeats the purpose.
The above example forces code using your class to use your smart pointer constructor, which means that if you switch to a new flavor of smart pointer, you change your class declaration and you have a decent chance of being finished. DO NOT assume your next boss or project will use stl, boost etc. plan for changing it someday.
Doing this for almost 30 years, I've paid a big price in time, pain and side effects to repair this when it was done wrong years ago.
Possibly the simplest solution. Based on the previous answer by Mohit Aron and incorporating dlf's suggestion.
How about this?
This answer is probably better, and the one I'll likely accept. But I also came up with a method that's uglier, but does still let everything still be inline and doesn't require a derived class:
Edit 2017-01-06: I changed this to make it clear that this idea is clearly and simply extensible to constructors that take arguments because other people were providing answers along those lines and seemed confused about this.