Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I call ::std::make_shared on a class with only protected or private constructors?

People also ask

What is std :: Make_shared?

std::make_sharedAllocates and constructs an object of type T passing args to its constructor, and returns an object of type shared_ptr<T> that owns and stores a pointer to it (with a use count of 1). This function uses ::new to allocate storage for the object.

What is Shared_from_this?

shared_from_this. returns a shared_ptr which shares ownership of *this. (public member function)

Where is std :: shared_ptr defined?

If your C++ implementation supports C++11 (or at least the C++11 shared_ptr ), then std::shared_ptr will be defined in <memory> . If your C++ implementation supports the C++ TR1 library extensions, then std::tr1::shared_ptr will likely be in <memory> (Microsoft Visual C++) or <tr1/memory> (g++'s libstdc++).

What does a shared pointer do?

"Shared pointer is a smart pointer (a C++ object wih overloaded operator*() and operator->()) that keeps a pointer to an object and a pointer to a shared reference count. Every time a copy of the smart pointer is made using the copy constructor, the reference count is incremented.


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.


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

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

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

How about this?

static std::shared_ptr<A> create()
{
    std::shared_ptr<A> pA(new A());
    return pA;
}

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

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