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 herenciaBaseque 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
Baseno 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_ptrtiene 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>cuandoSsea 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_uniquees 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