¿Cuál es el mejor método para pasar a shared_ptrde un tipo derivado a una función que toma a shared_ptrde un tipo base?
Generalmente paso shared_ptrs 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_castparece un poco excesivo para un elenco simple derivado a base. - Según tengo entendido,
dynamic_pointer_castcrea 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_ptrvalor por ejint foo(unique_ptr<bar> b). Las personas que llaman deben incluirstd::moveel valor en la función.Las funciones que extienden la vida útil de un objeto deben tomar un
shared_ptrvalor 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?dynamicyeso solo es necesario para abatir. Además, pasar el puntero derivado debería funcionar bien. Creará un nuevoshared_ptrcon 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_ptrpara pasar a la función? Estoy bastante seguro de que no hay forma de evitarlo.Respuestas:
Aunque
BaseyDerivedson 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_castserí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_ptral pasarlo afoo().void foo(const Base& base); [...] shared_ptr<Derived> spDerived = getDerived(); foo(*spDerived);Además, dado que los
shared_ptrtipos 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_castes 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_ptren 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
classes para parámetros de plantilla;structes para definir clases. (Esto es a lo sumo un 45% una broma.)Parece que te estás esforzando demasiado.
shared_ptres 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_ptrimplementación que envía Microsoft.También verifique que el
#includedel 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,#includeel compilador no podía ver que una clase se derivaba de la otra.fuente