¿Cuál es el mejor método para pasar a shared_ptr
de un tipo derivado a una función que toma a shared_ptr
de un tipo base?
Generalmente paso shared_ptr
s por referencia para evitar una copia innecesaria:
int foo(const shared_ptr<bar>& ptr);
pero esto no funciona si trato de hacer algo como
int foo(const shared_ptr<Base>& ptr);
...
shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);
Podría usar
foo(dynamic_pointer_cast<Base, Derived>(bar));
pero esto parece subóptimo por dos razones:
- A
dynamic_cast
parece un poco excesivo para un elenco simple derivado a base. - Según tengo entendido,
dynamic_pointer_cast
crea una copia (aunque temporal) del puntero para pasar a la función.
¿Existe una solución mejor?
Actualización para la posteridad:
Resultó ser un problema de falta de archivo de encabezado. Además, lo que estaba tratando de hacer aquí se considera un antipatrón. Generalmente,
Las funciones que no afectan la vida útil de un objeto (es decir, el objeto sigue siendo válido durante la duración de la función) deben tomar una referencia simple o un puntero, por ejemplo
int foo(bar& b)
.Las funciones que consumen un objeto (es decir, son los usuarios finales de un objeto dado) deben tomar un
unique_ptr
valor por ejint foo(unique_ptr<bar> b)
. Las personas que llaman deben incluirstd::move
el valor en la función.Las funciones que extienden la vida útil de un objeto deben tomar un
shared_ptr
valor por ejemplo, por ejemploint foo(shared_ptr<bar> b)
. Se aplica el consejo habitual para evitar referencias circulares .
Consulte la charla Back to Basics de Herb Sutter para obtener más detalles.
fuente
shared_ptr
? ¿Por qué no hay referencia constante de barra?dynamic
yeso solo es necesario para abatir. Además, pasar el puntero derivado debería funcionar bien. Creará un nuevoshared_ptr
con el mismo refcount (y lo aumentará) y un puntero a la base, que luego se une a la referencia constante. Sin embargo, dado que ya está tomando una referencia, no veo por qué quiere tomar unashared_ptr
. Toma unBase const&
y llamafoo(*bar)
.foo(bar)
) no funciona, al menos en MSVC 2010.shared_ptr
para pasar a la función? Estoy bastante seguro de que no hay forma de evitarlo.Respuestas:
Aunque
Base
yDerived
son indicadores covariantes y crudos, actuarán en consecuenciashared_ptr<Base>
y noshared_ptr<Derived>
son covariantes. El es la manera correcta y sencilla de manejar este problema.dynamic_pointer_cast
( Editar:
static_pointer_cast
sería más apropiado porque está transmitiendo de derivado a base, que es seguro y no requiere verificaciones de tiempo de ejecución. Consulte los comentarios a continuación).Sin embargo, si su
foo()
función no desea participar en la extensión de la vida útil (o, más bien, participar en la propiedad compartida del objeto), entonces es mejor aceptarconst Base&
y eliminar la referenciashared_ptr
al pasarlo afoo()
.void foo(const Base& base); [...] shared_ptr<Derived> spDerived = getDerived(); foo(*spDerived);
Además, dado que los
shared_ptr
tipos no pueden ser covariantes, las reglas de conversiones implícitas entre tipos de devolución covariantes no se aplican al devolver tipos deshared_ptr<T>
.fuente
shared_ptr<Derived>
se pueden convertir implícitamente enshared_ptr<Base>
, por lo que el código debería funcionar sin travesuras de casting.shared_ptr<Ty>
tiene un constructor que toma ashared_ptr<Other>
y hace la conversión apropiada siTy*
es implícitamente convertible aOther*
. Y si se necesita un yeso,static_pointer_cast
es el apropiado aquí, nodynamic_pointer_cast
.shared_ptr
's para evitar el recuento de referencias, entonces realmente no hay una buena razón para usar ashared_ptr
en primer lugar. Es mejor usarconst Base&
en su lugar.static_pointer_cast
, gracias.Esto también sucederá si ha olvidado especificar la herencia pública en la clase derivada, es decir, si como yo, escribe esto:
class Derived : Base { };
fuente
class
es para parámetros de plantilla;struct
es para definir clases. (Esto es a lo sumo un 45% una broma.)Parece que te estás esforzando demasiado.
shared_ptr
es barato de copiar; ese es uno de sus objetivos. Pasarlos por referencia no logra mucho. Si no desea compartir, pase el puntero sin procesar.Dicho esto, hay dos formas de hacer esto en las que puedo pensar:
foo(shared_ptr<Base>(bar)); foo(static_pointer_cast<Base>(bar));
fuente
shared_ptr
implementación que envía Microsoft.También verifique que el
#include
del archivo de encabezado que contiene la declaración completa de la clase derivada esté en su archivo fuente.Tuve este problema. El
std::shared<derived>
no lanzaría astd::shared<base>
. Había declarado hacia adelante ambas clases para poder contener punteros hacia ellas, pero como no tenía el,#include
el compilador no podía ver que una clase se derivaba de la otra.fuente