205

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?

Omnifarious
  • 50,447
  • 15
  • 117
  • 181

15 Answers15

116

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:

#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);
}

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.

Community
  • 1
  • 1
Omnifarious
  • 50,447
  • 15
  • 117
  • 181
  • 14
    Actually, I am a huge fan of those meaningless structures used only as *keys*. I prefer this to Luc's solution, but that might be my biais against inheritance. – Matthieu M. Nov 16 '11 at 07:43
  • 2
    Agreed, I like this better as well. – ildjarn Nov 16 '11 at 19:04
  • One advantage this has is that it works even if the constructor is supposed to be private. The other only works if the constructor is protected so that a derived class has access to it. – Omnifarious Nov 16 '11 at 20:18
  • 3
    @Berkus: Then make it `protected` instead of `private`. And by "it", I'm referring to the `this_is_private` class, which maybe should be renamed in such case. I usually call it `constructor_access` in my code. – dalle Feb 16 '14 at 14:02
  • Ah, right, thanks dalle, I believe I was in altered state of mind when I wrote that. Seems to work fine. – berkus Feb 27 '14 at 00:13
  • 1
    Sadly this doesn't work if your constructor takes real parameters; in this case you can simply pass `{}` for the private tag without having access to the type name (tested with g++ 4.9.0). Without real parameters it tries to construct `A` from {}, although I have no idea why, and fails. I think making the this_is_private constructor private and providing a static method to create it fixes it, as there should be no way to access this method from the outside unless you leak the type in a member function signature. – Stefan Jul 12 '14 at 17:37
  • 3
    Stefan, if you give `this_is_private` a private ctor you can make class A a friend. Seems to close the loophole. – Steven Kramer Jul 18 '14 at 08:12
  • 1
    Why is this answer not ranked at the top? – Brian Bi Nov 04 '15 at 22:50
  • 1
    @StevenKramer, indeed. I just change `this_is_private`'s definition to `class this_is_private { friend class A; this_is_private() {} };` and worked as expected. Note however that if `this_is_private`'s ctor is defined as `=default;` the loophole is back. – Tarc Dec 29 '16 at 16:30
  • 1
    @GusevSlava - Yes, this is a variant of that idiom. That idiom is conceived of as a way to avoid giving the full privileges of friendship. But in this case it's being used to grant a specific ability when friendship isn't possible. I think this, in fact, is a better use of this idiom. – Omnifarious Aug 07 '18 at 23:22
  • Very clever. One tweak: I'd like it more if it explicitly included the private c'tor (without the magic-class argument). Then the special public c'tor could be `template explicit A(const this_is_private&, Args&&... args) : A(std::forward(args)) {}`. That way you are saying that there's a private c'tor and then saying there's a special public c'tor that forwards its args. – Ben Mar 07 '19 at 20:14
  • 1
    @Ben - That's not a bad idea. I might incorporate it into the answer. – Omnifarious Mar 07 '19 at 21:53
  • 2
    This question already has enough alternative answers (though many of them appear to be duplicate variations of the same themes). I am using the PassKey idiom in other areas, and I found that the two top-voted answers here actually combine quite nicely. I added a private `make_shared_enabler` class which derives from my main class, but the main class' constructor takes a PassKey for that `make_shared_enabler` class. This allows me to hide the constructor from the public API by making it protected, while disallowing (additional) subclassing thanks to the PassKey. – monkey0506 Sep 13 '19 at 03:03
  • 1
    Why delete the copy constructor and assignment operator? – kervin Nov 09 '19 at 05:45
  • 2
    @Omnifarious: What's the point of the `int` in the `this_is_private` ctor? Why not simply `explicit this_is_private() =default;` – plexando Feb 07 '21 at 21:12
  • @plexando - I believe that I did this out of some vague fear that somehow the default constructor could be called even if it was private. It probably should be the way you think it should be. :-) – Omnifarious Feb 16 '21 at 03:44
  • @kervin - Because the point is to make sure that the object must always be created on the heap with a shared_ptr pointing to it. – Omnifarious Feb 16 '21 at 03:45
82

Looking at the requirements for std::make_shared in 20.7.2.2.6 shared_ptr creation [util.smartptr.shared.create], paragraph 1:

Requires: The expression ::new (pv) T(std::forward<Args>(args)...), where pv has type void* and points to storage suitable to hold an object of type T, shall be well formed. A shall be an allocator (17.6.3.5). The copy constructor and destructor of A shall not throw exceptions.

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 making A an interface or even a polymorphic type.

// 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>();
}
Luc Danton
  • 33,152
  • 5
  • 66
  • 110
  • 1
    Oh, that's a very clever answer, and possibly better than another one I had thought of. – Omnifarious Nov 16 '11 at 05:41
  • One question though, won't the shared_ptr delete an A and not a concrete_A, and couldn't this cause problems? – Omnifarious Nov 16 '11 at 05:51
  • 8
    Ahh, it's because `shared_ptr` stores a deleter at the time of instantiation, and if you're using `make_shared` the deleter absolutely has to be using the right type. – Omnifarious Nov 16 '11 at 06:23
  • I'm accepting your answer because it is a good answer. And while I think I like mine better, I think it's cheesy to accept your own answer when there is a perfectly acceptable answer that someone else made. – Omnifarious Nov 16 '11 at 20:16
  • 1
    @LucDanton Question is not about interfaces, as the title suggests he is also asking for a private ctor. Also, that's why I'm on this question anyway. Some old code having machiavelli classes, which has a private ctor and a create method returning a raw pointer, and I am trying to convert them to smart pointers. – zahir Aug 11 '15 at 07:09
  • @zahir I intended to mean interface in the broader meaning (e.g. class interface), although unfortunately I do use it with the narrower meaning in my answer. My point being that having a private member that you can use from non-friend code is a self-defeating goal by definition. It’s working as intended. – Luc Danton Aug 11 '15 at 13:49
  • @LucDanton Though I should admit that it is weird to have a _public method for a private member_ it is needed for [some loopholes](http://stackoverflow.com/questions/124880/is-it-possible-to-prevent-stack-allocation-of-an-object-and-only-allow-it-to-be) in the language – zahir Aug 11 '15 at 20:07
  • @LucDanton Mistakes were made – zahir Aug 12 '15 at 10:37
  • 2
    I like this approach (using it myself) but you do need a virtual destructor. It extends well to constructors with arguments (just provide a passthrough constructor). And if you are using _protected_ rather than _private_ you can make it completely invisible to users of the header. – Joe Steele Aug 28 '17 at 18:43
  • But doesn't this mean that theoretically anyone who can derive from `A` can make instances of it ? I mean, the initial intent was to prevent anyone from creating instances of `A` other than via `create` method. This solution is nice and all, but the very fact that it exists makes this whole idea flawed, or am I wrong ? – jcxz Jun 10 '19 at 08:37
  • @jcxz yes, but that's the same situation as in the question. That being said, this solution can also fit cases where `A` is only a public interface and all private or protected code moves to the `concrete_A` implementation. – Luc Danton Jun 10 '19 at 14:35
  • @LucDanton Yes that is true, this loophole exists also in the situation from the original question. Therefore I think workarounds like this one are pointless when the very concept they are trying to enable is flawed. To close the loophole, `A` has to be made final, but that in turn disallows this solution ... Not trying to discredit your answer, just wanted to point this out. I think a better way to go about implementing what the OP wanted is to use `PassKey` or intrusive pointers ... – jcxz Jun 11 '19 at 12:38
76

Possibly the simplest solution. Based on the previous answer by Mohit Aron and incorporating dlf's suggestion.

#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() {}  
};
Community
  • 1
  • 1
Mark Tolley
  • 1,098
  • 8
  • 12
  • 6
    if `A` has non-default constructors you will also need to expose them: `struct make_shared_enabler : public A { template make_shared_enabler(Args &&... args):A(std::forward(args)...) {} };`. This makes all private constructors of `A` visible as `make_shared_enabler` constructors. Using constructors inheritance feature (`using A::A;`) seems doesn't help here because constructors will be still private. – anton_rh Dec 14 '15 at 06:44
  • 3
    @anton_rh: you can't add template arguments to inner classes. See [here](http://stackoverflow.com/questions/4299314/member-template-in-local-class). – bobbel Feb 17 '16 at 20:20
  • 3
    Hm... Seems you are right. In my case struct was not local, but was a private struct: `class A { ... private: struct A_shared_enabler; }; class A::A_shared_enabler : public A { ... }`. See here http://cpp.sh/65qbr. – anton_rh Feb 18 '16 at 11:31
  • This works great. Is there any chance of making this an inheritable property, so this pattern doesn't have to be repeated multiple times? Particularly the version that exposes non-default constructors would be very interesting to me. The default version would "merely" require some syntactic construct that replaces A with whatever the class is which inherits the class. I'm not aware of anything like that, but I wouldn't be surprised to learn that it exists... – Kjeld Schmidt Jun 09 '19 at 09:28
41

Here's a neat solution for this:

#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>();
}
Jona
  • 12,642
  • 13
  • 82
  • 124
Mohit Aron
  • 411
  • 4
  • 2
  • 4
    I like this. It can be made a little simpler by defining `MakeSharedEnabler` locally inside `A::Create()`. – dlf Jul 29 '14 at 13:34
  • Awesome idea Mohit it helped me a lot. – Jnana Dec 17 '16 at 10:36
  • @dlf No you can't do this for non-template class as in an example. `A` class will be incomplete. – Soup Endless Feb 23 '21 at 11:44
  • @dlf I did misread this, thought you mean defining `MakeSharedEnabler` in `A`. Thou you can't define `MakeSharedEnabler ` inside `Create` either because it aint eclosing class `A`, you will get compile error. You should define `MakeSharedEnabler` before `Create` in order to make this work. Also you can't define `Create` inside class `A` as you will need `MakeSharedEnabler` definition to be able to convert `MakeSharedEnabler` into `A`, you do can return `shared_ptr< MakeSharedEnabler>` from `Create`, but this will be a bit uglier for client of the code and somewhate breaking encapsulation. – Soup Endless Feb 23 '21 at 12:12
  • For `template class A;` you do can define both `MakeSharedEnabler` and `Create` inside `A`. – Soup Endless Feb 23 '21 at 12:18
  • This should be the accepted answer, such simpler – Conrad Jones Mar 17 '21 at 20:58
14

How about this?

static std::shared_ptr<A> create()
{
    std::shared_ptr<A> pA(new A());
    return pA;
}
Sean
  • 4,676
  • 2
  • 22
  • 21
  • 13
    That works great. But `::std::make_shared` has functionality above and beyond simply making a shared_ptr to something. It allocates the reference count along with the object so they're located close to each other. I really, really want to use `::std::make_shared`. – Omnifarious Nov 16 '11 at 05:23
  • The deleted assigment and copy operators forbid this – Dani Nov 16 '11 at 05:23
  • 7
    This is really the most straightforward approach, even though it isn't really what the question was asking. make_shared does have some nice characteristics and I try to use it wherever possible, but in this situation it seems quite likely that the run-time performance advantages of make_shared do not outweigh the extra code complexity and ceremony actually required to use it. If you really need the performance of make_shared then go crazy, but don't overlook the simplicity of just using shared_ptr's constructor. – Kevin Mar 19 '14 at 17:18
  • Be careful about memory leaks though... see this question https://stackoverflow.com/a/14837300/2149539 – dgmz Jun 21 '18 at 11:52
  • @Omnifarious where is this documented? – smac89 Dec 28 '20 at 21:22
  • 1
    @smac89 - https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared#Notes - See the notes section, the first bullet point. It's a suggested implementation in the standard. – Omnifarious Jan 02 '21 at 21:00
13
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_;
};
alpha
  • 1,046
  • 10
  • 20
  • This is largely the same thing as Luc Danton's answer, although turning it into a local class is a nice touch. Some explanation to accompany the code could make this a much better answer. –  Jan 08 '15 at 07:08
  • Normally, I want to write such small function in header file but not cc file. Second, in practice, I use a macro which looks like #define SharedPtrCreate(T) template..... – alpha Jan 08 '15 at 08:27
  • Good answer. I'd even put that into a macro called like IMPLEMENT_CREATE_SHARED(ClassName) – ivan.ukr Feb 06 '20 at 08:15
  • One issue with this is that now it is a little bit more confusing what parameters create actually takes, and any errors will result in more confusing template messages. It's just one tradeoff from defining the helper class inside the actual create method. – Matias Grioni Apr 26 '21 at 23:46
8

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:

#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;
}

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.

#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;
}
Zsolt Rizsányi
  • 322
  • 2
  • 7
7

Ideally, I think the perfect solution would require additions to the C++ standard. Andrew Schepler proposes the following:

(Go here for the whole thread)

we can borrow an idea from boost::iterator_core_access. I propose a new class std::shared_ptr_access with no public or protected members, and to specify that for std::make_shared(args...) and std::alloc_shared(a, args...), the expressions ::new(pv) T(forward(args)...) and ptr->~T() must be well-formed in the context of std::shared_ptr_access.

An implementation of std::shared_ptr_access might look like:

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;
    };
}

Usage

If/when the above is added to the standard, we would simply do:

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;
};

If this also sounds like an important addition to the standard to you, feel free to add your 2 cents to the linked isocpp Google Group.

Boris Dalstein
  • 5,395
  • 3
  • 26
  • 48
  • 1
    I think it's a good addition to the standard, but it's not important enough for me to take the time to join the Google Group and comment and then pay attention to that group and the comment. :-) – Omnifarious Aug 08 '18 at 20:41
4

I realise this thread is rather old, but I found an answer that does not require inheritance or extra arguments to the constructor that I couldn't see elsewhere. It is not portable though:

#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());
}

I have tested on windows and linux, it may need tweaking for different platforms.

ashleysmithgpu
  • 1,537
  • 14
  • 36
  • 1
    I'm tempted to -1 it for lack of portability. The other answers (particularly the 'key class' answers) are fairly elegant and the non-portable answer very ugly. I can't think of a reason you'd use the non-portable answer. It's not faster or anything like that. – Omnifarious Dec 24 '16 at 06:38
  • @Omnifarious It is indeed non-portable and I wouldn't recommend, but I believe this is in fact the semantically most correct solution. In [my answer](https://stackoverflow.com/a/51266259/1951907), I link to a proposal of adding `std::shared_ptr_access` to the standard, which could be seen as allowing to do the above in a simple and portable way. – Boris Dalstein Jul 10 '18 at 13:20
4

If you also want to enable a constuctor that takes arguments, this may help a bit.

#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");
}
demo
  • 41
  • 1
4

I had the same problem, but none of the existing answers were really satisfactory as I need to pass arguments to the protected constructor. Moreover, I need to do this for several classes, each taking different arguments.

To that effect, and building on several of the existing answers which all use similar methods, I present this little nugget:

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 )... );
}
Matthew
  • 2,121
  • 18
  • 20
3

There's a more hairy and interesting problem that happens when you have two strictly related classes A and B that work together.

Say A is the "master class" and B its "slave". If you want to restrict instantiation of B only to A, you'd make B's constructor private, and friend B to A like this

class B
{
public:
    // B your methods...

private:
    B();
    friend class A;
};

Unfortunately calling std::make_shared<B>() from a method of A will make the compiler complain about B::B() being private.

My solution to this is to create a public Pass dummy class (just like nullptr_t) inside B that has private constructor and is friend with A and make B's constructor public and add Pass to its arguments, like this.

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);
  }
};
keebus
  • 810
  • 7
  • 13
3

[Edit] I read through the thread noted above on a standardized std::shared_ptr_access<> proposal. Within there was a response noting a fix to std::allocate_shared<> and an example of its use. I've adapted it to a factory template below, and tested it under gcc C++11/14/17. It works with std::enable_shared_from_this<> as well, so would obviously be preferable to my original solution in this answer. Here it is...

#include <iostream>
#include <memory>

class Factory final {
public:
    template<typename T, typename... A>
    static std::shared_ptr<T> make_shared(A&&... args) {
        return std::allocate_shared<T>(Alloc<T>(), std::forward<A>(args)...);
    }
private:
    template<typename T>
    struct Alloc : std::allocator<T> {
        template<typename U, typename... A>
        void construct(U* ptr, A&&... args) {
            new(ptr) U(std::forward<A>(args)...);
        }
        template<typename U>
        void destroy(U* ptr) {
            ptr->~U();
        }
    };  
};

class X final : public std::enable_shared_from_this<X> {
    friend class Factory;
private:
    X()      { std::cout << "X() addr=" << this << "\n"; }
    X(int i) { std::cout << "X(int) addr=" << this << " i=" << i << "\n"; }
    ~X()     { std::cout << "~X()\n"; }
};

int main() {
    auto p1 = Factory::make_shared<X>(42);
    auto p2 = p1->shared_from_this();
    std::cout << "p1=" << p1 << "\n"
              << "p2=" << p2 << "\n"
              << "count=" << p1.use_count() << "\n";
}

[Orig] I found a solution using the shared pointer aliasing constructor. It allows both the ctor and dtor to be private, as well as use of the final specifier.

#include <iostream>
#include <memory>

class Factory final {
public:
    template<typename T, typename... A>
    static std::shared_ptr<T> make_shared(A&&... args) {
        auto ptr = std::make_shared<Type<T>>(std::forward<A>(args)...);
        return std::shared_ptr<T>(ptr, &ptr->type);
    }
private:
    template<typename T>
    struct Type final {
        template<typename... A>
        Type(A&&... args) : type(std::forward<A>(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
        ~Type() { std::cout << "~Type()\n"; }
        T type;
    };
};

class X final {
    friend struct Factory::Type<X>;  // factory access
private:
    X()      { std::cout << "X() addr=" << this << "\n"; }
    X(int i) { std::cout << "X(...) addr=" << this << " i=" << i << "\n"; }
    ~X()     { std::cout << "~X()\n"; }
};

int main() {
    auto ptr1 = Factory::make_shared<X>();
    auto ptr2 = Factory::make_shared<X>(42);
}

Note that the approach above doesn't play well with std::enable_shared_from_this<> because the initial std::shared_ptr<> is to the wrapper and not the type itself. We can address this with an equivalent class that is compatible with the factory...

#include <iostream>
#include <memory>

template<typename T>
class EnableShared {
    friend class Factory;  // factory access
public:
    std::shared_ptr<T> shared_from_this() { return weak.lock(); }
protected:
    EnableShared() = default;
    virtual ~EnableShared() = default;
    EnableShared<T>& operator=(const EnableShared<T>&) { return *this; }  // no slicing
private:
    std::weak_ptr<T> weak;
};

class Factory final {
public:
    template<typename T, typename... A>
    static std::shared_ptr<T> make_shared(A&&... args) {
        auto ptr = std::make_shared<Type<T>>(std::forward<A>(args)...);
        auto alt = std::shared_ptr<T>(ptr, &ptr->type);
        assign(std::is_base_of<EnableShared<T>, T>(), alt);
        return alt;
    }
private:
    template<typename T>
    struct Type final {
        template<typename... A>
        Type(A&&... args) : type(std::forward<A>(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
        ~Type() { std::cout << "~Type()\n"; }
        T type;
    };
    template<typename T>
    static void assign(std::true_type, const std::shared_ptr<T>& ptr) {
        ptr->weak = ptr;
    }
    template<typename T>
    static void assign(std::false_type, const std::shared_ptr<T>&) {}
};

class X final : public EnableShared<X> {
    friend struct Factory::Type<X>;  // factory access
private:
    X()      { std::cout << "X() addr=" << this << "\n"; }
    X(int i) { std::cout << "X(...) addr=" << this << " i=" << i << "\n"; }
    ~X()     { std::cout << "~X()\n"; }
};

int main() {
    auto ptr1 = Factory::make_shared<X>();
    auto ptr2 = ptr1->shared_from_this();
    std::cout << "ptr1=" << ptr1.get() << "\nptr2=" << ptr2.get() << "\n";
}

Lastly, somebody said clang complained about Factory::Type being private when used as a friend, so just make it public if that's the case. Exposing it does no harm.

user1715587
  • 135
  • 8
1

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.

class A;
typedef std::shared_ptr<A> APtr;
class A
{
    template<class T>
    friend class std::_Ref_count_obj;
public:
    APtr create()
    {
        return std::make_shared<A>();
    }
private:
    A()
    {}
};

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

#define SHARED_PTR_DECL(T) \
class T; \
typedef std::shared_ptr<T> ##T##Ptr;

#define FRIEND_STD_MAKE_SHARED \
template<class T> \
friend class std::_Ref_count_obj;

Then your class declaration looks fairly simple. You can make a single macro for declaring the ptr and the class if you prefer.

SHARED_PTR_DECL(B);
class B
{
    FRIEND_STD_MAKE_SHARED
public:
    BPtr create()
    {
        return std::make_shared<B>();
    }
private:
    B()
    {}
};

This is actually an important issue. To make maintainable, portable code you need to hide as much of the implementation as possible.

typedef std::shared_ptr<A> APtr;

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.

brtip
  • 37
  • 1
  • 2
    `std::_Ref_count_obj` is an implementation detail. That means while this solution might work for you, for now, on your platform. But it might not work for others and might stop working any time your compiler updates or maybe even if you just change compilation flags. – François Andrieux Dec 27 '18 at 20:32
-3
#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;
}
spise
  • 50
  • 1