How do I call ::std::make_shared on a class with only protected or private constructors?
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)...)
, wherepv
has typevoid*
and points to storage suitable to hold an object of typeT
, shall be well formed.A
shall be an allocator (17.6.3.5). The copy constructor and destructor ofA
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() {}
};