[Редактировать] Я прочитал вышеупомянутую ветку стандартного предложения std::shared_ptr_access<>
. Внутри был ответ, отмечающий исправление std::allocate_shared<>
и пример его использования. Я адаптировал его к заводскому шаблону ниже и протестировал его в gcc C ++ 11/14/17. Он также работает с std::enable_shared_from_this<>
, поэтому, очевидно, предпочтительнее моего исходного решения в этом ответе. Вот оно ...
#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] Я нашел решение, используя конструктор псевдонимов общего указателя. Это позволяет ctor и dtor быть приватными, а также использовать финальный спецификатор.
#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);
}
Обратите внимание, что описанный выше подход не очень хорошо работает с std::enable_shared_from_this<>
, потому что начальный std::shared_ptr<>
относится к оболочке, а не к самому типу. Мы можем решить эту проблему с помощью эквивалентного класса, совместимого с фабрикой ...
#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";
}
Наконец, кто-то сказал, что clang жаловался на то, что Factory :: Type является приватным при использовании в качестве друга, поэтому просто сделайте его публичным, если это так. Разоблачение не вредит.