I have this code that doesn\'t work, but I think the intent is clear:
testmakeshared.cpp
#include
class A {
public:
stat
[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
#include
class Factory final {
public:
template
static std::shared_ptr make_shared(A&&... args) {
return std::allocate_shared(Alloc(), std::forward(args)...);
}
private:
template
struct Alloc : std::allocator {
template
void construct(U* ptr, A&&... args) {
new(ptr) U(std::forward(args)...);
}
template
void destroy(U* ptr) {
ptr->~U();
}
};
};
class X final : public std::enable_shared_from_this {
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(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
#include
class Factory final {
public:
template
static std::shared_ptr make_shared(A&&... args) {
auto ptr = std::make_shared>(std::forward(args)...);
return std::shared_ptr(ptr, &ptr->type);
}
private:
template
struct Type final {
template
Type(A&&... args) : type(std::forward(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
~Type() { std::cout << "~Type()\n"; }
T type;
};
};
class X final {
friend struct Factory::Type; // 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();
auto ptr2 = Factory::make_shared(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
#include
template
class EnableShared {
friend class Factory; // factory access
public:
std::shared_ptr shared_from_this() { return weak.lock(); }
protected:
EnableShared() = default;
virtual ~EnableShared() = default;
EnableShared& operator=(const EnableShared&) { return *this; } // no slicing
private:
std::weak_ptr weak;
};
class Factory final {
public:
template
static std::shared_ptr make_shared(A&&... args) {
auto ptr = std::make_shared>(std::forward(args)...);
auto alt = std::shared_ptr(ptr, &ptr->type);
assign(std::is_base_of, T>(), alt);
return alt;
}
private:
template
struct Type final {
template
Type(A&&... args) : type(std::forward(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
~Type() { std::cout << "~Type()\n"; }
T type;
};
template
static void assign(std::true_type, const std::shared_ptr& ptr) {
ptr->weak = ptr;
}
template
static void assign(std::false_type, const std::shared_ptr&) {}
};
class X final : public EnableShared {
friend struct Factory::Type; // 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();
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.