106 votes

unique_ptr à une classe dérivée comme argument à une fonction qui prend un unique_ptr à une classe de base

J'essaie d'utiliser un unique_ptr à la classe dérivée dans une fonction qui prend un unique_ptr à une classe de base. Quelque chose comme :

class Base {};

class Derived : public Base {};

void f(unique_ptr<Base> const &base) {}

…

unique_ptr<Derived> derived = unique_ptr<Derived>(new Derived);
f(derived);

Si je comprends bien cette réponse correctement, ce code devrait fonctionner, mais il provoque les erreurs de compilation suivantes :

erreur C2664 : 'f' : impossible de convertir le paramètre 1 de 'std::unique_ptr<_Ty>' en 'const std::unique_ptr<_Ty> &'.

IntelliSense : il n'existe pas de conversion appropriée définie par l'utilisateur de "std::unique_ptr<Derived, std::default_delete<Derived>>" à "const std::unique_ptr<Base, std::default_delete<Base>>".

Si je change f pour prendre unique_ptr<Derived> const &derived cela fonctionne bien, mais ce n'est pas ce que je veux.

Est-ce que je fais quelque chose de mal ? Que puis-je faire pour contourner ce problème ?

J'utilise Visual Studio 2012.

109voto

Kerrek SB Points 194696

Vous avez trois options :

  1. Renoncer à la propriété. Ainsi, votre variable locale n'aura plus accès à l'objet dynamique après l'appel de fonction ; l'objet a été transféré au destinataire de l'appel :

    f(std::move(derived));
  2. Changez la signature de f :

    void f(std::unique_ptr<Derived> const &);
  3. Changez le type de votre variable :

    std::unique_ptr<base> derived = std::unique_ptr<Derived>(new Derived);

    Ou, bien sûr, juste :

    std::unique_ptr<base> derived(new Derived);

    Ou même :

    std::unique_ptr<base> derived = std::make_unique<Derived>();
  4. Mise à jour : Ou, comme recommandé dans les commentaires, ne transférez pas du tout la propriété :

    void f(Base & b);
    
    f(*derived);

53voto

David Points 273

J'avais l'option 1 de la réponse acceptée et j'avais toujours la même erreur de compilation. Je me suis tapé la tête contre le mur pendant plus d'une heure et j'ai finalement réalisé que j'avais

class Derived : Base {};

au lieu de

class Derived : public Base {};

14voto

hmjd Points 76411

Une solution possible est de changer le type de l'argument pour qu'il soit un Base const* et passer derived.get() à la place. Il n'y a pas de transfert de propriété avec unique_ptr const<Base>& (et le unique_ptr n'est pas modifié), de sorte que le passage à l'option Base const* ne change pas le sens.


Herb Sutter discute longuement du passage d'arguments de type pointeur intelligent dans le document suivant Paramètres du pointeur intelligent . Un extrait de l'article en lien fait référence à cette situation exacte :

Passage d'un const unique_ptr<widget>& est étrange car il ne peut accepter que l'un ou l'autre null ou un widget dont la durée de vie est gérée dans le code d'appel via une fonction unique_ptr et l'appelé ne devrait généralement pas se soucier du choix de gestion de la durée de vie de l'appelant. Passage de widget* couvre un sur-ensemble strict de ces cas et peut accepter " null ou un widget "quelle que soit la politique de durée de vie utilisée par l'appelant.

-2voto

AlQuemist Points 395

Une autre façon est de changer la signature de f et l'utiliser d'une manière légèrement différente :

void f(Base* base_ptr) {
    // take ownership inside the function
    std::unique_ptr<Base> base {base_ptr};
    // ...
}

// ...
auto derived = std::make_unique<Derived>();
f(derived.release());  // release ownership

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X