I have this code that doesn\'t work, but I think the intent is clear:
testmakeshared.cpp
#include
class A {
public:
stat
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);
}
};
How about this?
static std::shared_ptr<A> create()
{
std::shared_ptr<A> pA(new A());
return pA;
}
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");
}
You can use this:
class CVal
{
friend std::shared_ptr<CVal>;
friend std::_Ref_count<CVal>;
public:
static shared_ptr<CVal> create()
{
shared_ptr<CVal> ret_sCVal(new CVal());
return ret_sCVal;
}
protected:
CVal() {};
~CVal() {};
};
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_;
};
[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.