Escribí el siguiente código que usa unique_ptr<Derived>
donde unique_ptr<Base>
se espera a
class Base {
int i;
public:
Base( int i ) : i(i) {}
int getI() const { return i; }
};
class Derived : public Base {
float f;
public:
Derived( int i, float f ) : Base(i), f(f) {}
float getF() const { return f; }
};
void printBase( unique_ptr<Base> base )
{
cout << "f: " << base->getI() << endl;
}
unique_ptr<Base> makeBase()
{
return make_unique<Derived>( 2, 3.0f );
}
unique_ptr<Derived> makeDerived()
{
return make_unique<Derived>( 2, 3.0f );
}
int main( int argc, char * argv [] )
{
unique_ptr<Base> base1 = makeBase();
unique_ptr<Base> base2 = makeDerived();
printBase( make_unique<Derived>( 2, 3.0f ) );
return 0;
}
y que esperaba que este código no compila, porque según mi entendimiento unique_ptr<Base>
y unique_ptr<Derived>
son tipos no relacionados y unique_ptr<Derived>
no es, de hecho, derivado de unique_ptr<Base>
lo que la asignación no debería funcionar.
Pero gracias a algo de magia funciona, y no entiendo por qué, o incluso si es seguro hacerlo. ¿Alguien puede explicar por favor?
c++
templates
inheritance
unique-ptr
Youda008
fuente
fuente
unique_ptr
, sería bastante inútil en presencia de herenciaBase
que no tiene destructor virtual.Respuestas:
El poco de magia que estás buscando es el constructor de conversión # 6 aquí :
Permite construir un
std::unique_ptr<T>
implícitamente a partir de unstd::unique_ptr<U>
if expirado (pasar por alto los eliminadores para mayor claridad):Es decir, imita las conversiones implícitas de puntero sin formato, incluidas las conversiones derivadas a base, y hace lo que espera ™ de forma segura (en términos de vida útil: aún debe asegurarse de que el tipo base se pueda eliminar polimórficamente).
fuente
Base
no llamará al destructor deDerived
, por lo que no estoy seguro de si es realmente seguro. (Es cierto que no es menos seguro que un puntero en bruto).Porque
std::unique_ptr
tiene un constructor de conversión comoy
A
Derived*
podría convertirBase*
implícitamente, entonces el constructor de conversión podría aplicarse para este caso. Entonces astd::unique_ptr<Base>
podría convertirse de astd::unique_ptr<Derived>
implícitamente tal como lo hace el puntero sin formato. (Tenga en cuenta questd::unique_ptr<Derived>
tiene que ser un valor para construirstd::unique_ptr<Base>
debido a la característica destd::unique_ptr
).fuente
Puede construir implícitamente una
std::unique_ptr<T>
instancia a partir de un valor destd::unique_ptr<S>
cuandoS
sea convertible aT
. Esto se debe al constructor # 6 aquí . La propiedad se transfiere en este caso.En su ejemplo, solo tiene valores de tipo
std::uinque_ptr<Derived>
(porque el valor de retorno destd::make_unique
es un valor r), y cuando lo usa como astd::unique_ptr<Base>
, se invoca el constructor mencionado anteriormente. Por lo tanto, losstd::unique_ptr<Derived>
objetos en cuestión solo viven durante un corto período de tiempo, es decir, se crean, luego la propiedad se pasa alstd::unique_ptr<Base>
objeto que se usa más adelante.fuente