web-dev-qa-db-fra.com

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

J'essaie d'utiliser un unique_ptr à une 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 , 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: aucune conversion définie par l'utilisateur appropriée de "std :: unique_ptr <Derived, std :: default_delete <Derived>>" vers "const std :: unique_ptr <Base, std :: default_delete <Base>>" n'existe pas

Si je change f pour prendre unique_ptr<Derived> const &derived, ça marche 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.

54
svick

Vous avez trois options:

  1. Renoncez à la propriété. Cela laissera votre variable locale sans accès à l'objet dynamique après l'appel de fonction; l'objet a été transféré à l'appelé:

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

    void f(std::unique_ptr<Derived> const &);
    
  3. Modifiez 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. pdate: Ou, comme recommandé dans les commentaires, ne transférez pas du tout la propriété:

    void f(Base & b);
    
    f(*derived);
    
59
Kerrek SB

Une solution possible consiste à changer le type de l'argument pour qu'il soit un Base const*, Et à passer à la place derived.get(). Il n'y a pas de transfert de propriété avec unique_ptr const<Base>& (Et le unique_ptr N'est pas modifié), donc le passage à un Base const* Ne change pas la signification.


Herb Sutter discute longuement des arguments du pointeur intelligent dans Paramètres du pointeur intelligent . Un extrait de l'article lié fait référence à cette situation exacte:

Passer un const unique_ptr<widget>& Est étrange car il ne peut accepter que null ou widget dont la durée de vie est gérée dans le code appelant via un unique_ptr, Et l'appelé ne devrait généralement pas se soucier du choix de gestion à vie de l'appelant. La réussite de widget* Couvre un sur-ensemble strict de ces cas et peut accepter "null ou widget" quelle que soit la politique de durée de vie que l'appelant utilise.

9
hmjd

J'avais l'option # 1 de la réponse acceptée et j'avais toujours la même erreur de compilation. Je me suis cogné 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 {};
1
David