¿Cómo llamo ::std::make_shared en una clase que solo tiene constructores protegidos o privados?
Tengo este código que no funciona, pero creo que la intención es clara:
pruebamakeshared.cpp
#include <memory>
class A {
public:
static ::std::shared_ptr<A> create() {
return ::std::make_shared<A>();
}
protected:
A() {}
A(const A &) = delete;
const A &operator =(const A &) = delete;
};
::std::shared_ptr<A> foo()
{
return A::create();
}
Pero me sale este error cuando lo compilo:
g++ -std=c++0x -march=native -mtune=native -O3 -Wall testmakeshared.cpp
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:52:0,
from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/memory:86,
from testmakeshared.cpp:1:
testmakeshared.cpp: In constructor ‘std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc) [with _Tp = A, _Alloc = std::allocator<A>, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’:
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:518:8: instantiated from ‘std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:986:35: instantiated from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:313:64: instantiated from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:531:39: instantiated from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:547:42: instantiated from ‘std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = A, _Args = {}]’
testmakeshared.cpp:6:40: instantiated from here
testmakeshared.cpp:10:8: error: ‘A::A()’ is protected
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:400:2: error: within this context
Compilation exited abnormally with code 1 at Tue Nov 15 07:32:58
Este mensaje básicamente dice que algún método aleatorio que se encuentra en la parte inferior de la pila de creación de instancias de la plantilla ::std::make_shared
no puede acceder al constructor porque está protegido.
Pero realmente quiero usar ambos ::std::make_shared
y evitar que alguien cree un objeto de esta clase que no esté señalado por un archivo ::std::shared_ptr
. ¿Hay alguna forma de lograr esto?
Esta respuesta probablemente sea mejor y probablemente aceptaré. Pero también se me ocurrió un método que es más feo, pero que aún permite que todo esté en línea y no requiere una clase derivada:
#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);
}
Editar 2017-01-06: Cambié esto para dejar en claro que esta idea es clara y simplemente extensible a los constructores que aceptan argumentos porque otras personas estaban brindando respuestas en ese sentido y parecían confundidas al respecto.
Mirando los requisitos para std::make_shared
la creación de 20.7.2.2.6shared_ptr [util.smartptr.shared.create], párrafo 1:
Requiere: La expresión
::new (pv) T(std::forward<Args>(args)...)
, dondepv
tiene tipovoid*
y apunta a un almacenamiento adecuado para contener un objeto de tipoT
, debe estar bien formada.A
será un asignador (17.6.3.5). El constructor y destructor de copiasA
no generarán excepciones.
Dado que el requisito se especifica incondicionalmente en términos de esa expresión y cosas como el alcance no se tienen en cuenta, creo que trucos como la amistad están descartados.
Una solución simple es derivar de A
. Esto no requiere necesariamente crear A
una interfaz o incluso un tipo polimórfico.
// 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>();
}