J'ai écrit le code suivant qui utilise unique_ptr<Derived>
où un unique_ptr<Base>
est attendu
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;
}
et je m'attendais à ce que ce code ne se compile pas, car selon ma compréhension, unique_ptr<Base>
il unique_ptr<Derived>
s'agit de types non liés et unique_ptr<Derived>
ne sont en fait pas dérivés de unique_ptr<Base>
sorte que l'affectation ne devrait pas fonctionner.
Mais grâce à la magie, cela fonctionne, et je ne comprends pas pourquoi, ou même si c'est sûr de le faire. Quelqu'un peut-il expliquer s'il vous plaît?
c++
templates
inheritance
unique-ptr
Youda008
la source
la source
unique_ptr
serait plutôt inutile en présence de l'héritageBase
n'a pas de destructeur virtuel.Réponses:
Le peu de magie que vous recherchez est le constructeur de conversion # 6 ici :
Il permet de construire
std::unique_ptr<T>
implicitement à partir d'unstd::unique_ptr<U>
if expirant (en glissant sur les délétères pour plus de clarté):Autrement dit, il imite les conversions implicites de pointeurs bruts, y compris les conversions dérivées en base, et fait ce que vous attendez ™ en toute sécurité (en termes de durée de vie - vous devez toujours vous assurer que le type de base peut être supprimé par polymorphisme).
la source
Base
n'appellera pas le destructeur deDerived
, donc je ne sais pas si c'est vraiment sûr. (Il n'est pas moins sûr que le pointeur brut, certes.)Parce que
std::unique_ptr
possède un constructeur de conversionet
A
Derived*
pourrait être convertiBase*
implicitement, puis le constructeur de conversion pourrait être appliqué dans ce cas. Ensuite, unstd::unique_ptr<Base>
pourrait être converti à partir d'unstd::unique_ptr<Derived>
implicitement comme le fait le pointeur brut. (Notez que lestd::unique_ptr<Derived>
doit être une valeur r pour la construction enstd::unique_ptr<Base>
raison de la caractéristique destd::unique_ptr
.)la source
Vous pouvez implicitement construire un
std::unique_ptr<T>
exemple d'un rvalue destd::unique_ptr<S>
chaque foisS
est convertibleT
. Cela est dû au constructeur # 6 ici . La propriété est transférée dans ce cas.Dans votre exemple, vous ne disposez que de rvalues de type
std::uinque_ptr<Derived>
(car la valeur de retour destd::make_unique
est une rvalue), et lorsque vous l'utilisez comme astd::unique_ptr<Base>
, le constructeur mentionné ci-dessus est appelé. Lesstd::unique_ptr<Derived>
objets en question ne vivent donc que pendant une courte période, c'est-à-dire qu'ils sont créés, puis la propriété est transférée à l'std::unique_ptr<Base>
objet qui est utilisé plus loin.la source