Pourquoi unique_ptr <Derived> est-il implicitement converti en unique_ptr <Base>?


21

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?


3
les pointeurs intelligents doivent enrichir ce que les pointeurs peuvent ne pas limiter. Si cela n'était pas possible, ce unique_ptrserait plutôt inutile en présence de l'héritage
— idclev 463035818

3
"Mais grâce à un peu de magie ça marche" . Presque, vous avez obtenu UB car Basen'a pas de destructeur virtuel.
— Jarod42

Réponses:


25

Le peu de magie que vous recherchez est le constructeur de conversion # 6 ici :

template<class U, class E>
unique_ptr(unique_ptr<U, E> &&u) noexcept;

Il permet de construire std::unique_ptr<T>implicitement à partir d'un std::unique_ptr<U> if expirant (en glissant sur les délétères pour plus de clarté):

unique_ptr<U, E>::pointer est implicitement convertible en pointer

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).


2
AFAIK le délétère de Basen'appellera pas le destructeur de Derived, donc je ne sais pas si c'est vraiment sûr. (Il n'est pas moins sûr que le pointeur brut, certes.)
— cpplearner

14

Parce que std::unique_ptrpossède un constructeur de conversion

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;

et

Ce constructeur ne participe à la résolution de surcharge que si toutes les conditions suivantes sont remplies:

a) unique_ptr<U, E>::pointerest implicitement convertible enpointer

...

A Derived*pourrait être converti Base*implicitement, puis le constructeur de conversion pourrait être appliqué dans ce cas. Ensuite, un std::unique_ptr<Base>pourrait être converti à partir d'un std::unique_ptr<Derived>implicitement comme le fait le pointeur brut. (Notez que le std::unique_ptr<Derived>doit être une valeur r pour la construction en std::unique_ptr<Base>raison de la caractéristique de std::unique_ptr.)


7

Vous pouvez implicitement construire un std::unique_ptr<T>exemple d'un rvalue de std::unique_ptr<S>chaque fois Sest convertible T. 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 de std::make_uniqueest une rvalue), et lorsque vous l'utilisez comme a std::unique_ptr<Base>, le constructeur mentionné ci-dessus est appelé. Les std::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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.