Eu escrevi o seguinte código que usa unique_ptr<Derived>
onde unique_ptr<Base>
é esperado
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;
}
e eu esperava este código não compilar, porque de acordo com o meu entendimento unique_ptr<Base>
e unique_ptr<Derived>
são tipos não relacionados e unique_ptr<Derived>
não é na verdade derivado de unique_ptr<Base>
modo a atribuição não deve funcionar.
Mas, graças a alguma mágica, funciona, e eu não entendo o porquê, ou mesmo se é seguro fazê-lo. Alguém pode explicar por favor?
c++
templates
inheritance
unique-ptr
Youda008
fonte
fonte
unique_ptr
seria bastante inútil na presença de herançaBase
não possui destruidor virtual.Respostas:
O pouco de mágica que você está procurando é o construtor de conversão # 6 aqui :
Permite construir
std::unique_ptr<T>
implicitamente a partir de umstd::unique_ptr<U>
if expirante (encobrir deleters para maior clareza):Ou seja, ele imita conversões implícitas de ponteiros brutos, incluindo conversões derivadas da base, e faz o que você espera ™ com segurança (em termos de vida útil - você ainda precisa garantir que o tipo de base possa ser excluído polimorficamente).
fonte
Base
, não chamará o destruidor deDerived
, então não tenho certeza se é realmente seguro. (Não é menos seguro do que ponteiro bruto, reconhecidamente.)Porque
std::unique_ptr
tem um construtor de conversão comoe
A
Derived*
pode converter paraBase*
implicitamente, então o construtor de conversão pode ser aplicado para este caso. Então, umstd::unique_ptr<Base>
poderia ser convertido de umstd::unique_ptr<Derived>
implicitamente, assim como o ponteiro bruto. (Observe que o valorstd::unique_ptr<Derived>
deve ser um valor de construçãostd::unique_ptr<Base>
devido à característica destd::unique_ptr
.)fonte
Você pode construir implicitamente uma
std::unique_ptr<T>
instância a partir de um rvalor de whenstd::unique_ptr<S>
forS
convertível emT
. Isto é devido ao construtor # 6 aqui . A propriedade é transferida neste caso.No seu exemplo, você possui apenas rvalues do tipo
std::uinque_ptr<Derived>
(porque o valor de retornostd::make_unique
é um rvalue) e, quando você usa isso como astd::unique_ptr<Base>
, o construtor mencionado acima é chamado. Osstd::unique_ptr<Derived>
objetos em questão, portanto, vivem apenas por um curto período de tempo, ou seja, são criados, e a propriedade é passada para ostd::unique_ptr<Base>
objeto que é usado posteriormente.fonte