IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

unique_ptr, nécessite un move au return ?


Sujet :

C++

  1. #1
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut unique_ptr, nécessite un move au return ?
    Bonjour,

    J'ai actuellement une fonction qui, une fois simplifiée, ressemble à ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <memory>
     
    struct Base {};
     
    struct Derived : Base {};
     
    std::unique_ptr<Base> foo() {
        std::unique_ptr<Derived> ptr;
        return std::move(ptr);
    }
    Ma question est : pourquoi le move est-il nécessaire ?

    Pourquoi le code-suivant ne fonctionne-t-il pas ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <memory>
     
    struct Base {};
     
    struct Derived : Base {};
     
    std::unique_ptr<Base> foo() {
        std::unique_ptr<Derived> ptr;
        return ptr;
    }
    En effet, il me semblait que le move était implicite au moment d'un return.

    Où me trompe-je ?

    Je vous remercie d'avance,
    Equinoxe

  2. #2
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bonjour,

    Le problème vient du fait que foo() retourne un unique_ptr<Base> alors que ptr est un unique_ptr<Derived>. Donc dans le return, le compilateur est obligé d'ajouter la construction d'un temporaire de type unique_ptr<Base> et donc d'appeler le constructeur d'unique_ptr prenant un const unique_ptr<T>& en paramètre, qui est explicitement déclaré =delete.

    En fait on est dans le même cas que dans le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    std::unique_ptr<Base> foo() {
        std::unique_ptr<Derived> ptr(new Derived);
        //std::unique_ptr<Base> ptrbase(ptr); // erreur ici, ptr est une lvale value
        std::unique_ptr<Base> ptrbase(std::move(ptr)); // ok, std::move(ptr) est une rvalue
        return ptrbase;
    }
    Ceci-dit je soupçonne très fortement que la véritable intention est de pouvoir utiliser le polymorphisme Base/Dérivé et dans ce cas l'utilisation correcte est en fait la suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::unique_ptr<Base> foo() 
    {
        std::unique_ptr<Base> ptr(new Derived);
        //...
        return ptr;
    }

  3. #3
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    Si la fonction ne fait vraiment que ça (ou équivalent), l'écrire en une ligne permet au compilateur d'être sur que c'est un objet "temporaire" et va donc utiliser le move-constructor automatiquement à la place du constructeur par copie (si je ne me trompe pas, j'ai pas vérifié en compilant) :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    std::unique_ptr<Base> foo() 
    {
        return std::unique_ptr<Base> ptr(new Derived);
    }

  4. #4
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Je ne comprends pas ta solution Klaim, si tu retournes un objet du bon type (un unique_ptr<Base>) comme dans ton code alors il n'y a pas du tout besoin de l'écrire en une ligne...

  5. #5
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Bonjour,

    Le problème vient du fait que foo() retourne un unique_ptr<Base> alors que ptr est un unique_ptr<Derived>. Donc dans le return, le compilateur est obligé d'ajouter la construction d'un temporaire de type unique_ptr<Base> et donc d'appeler le constructeur d'unique_ptr prenant un const unique_ptr<T>& en paramètre, qui est explicitement déclaré =delete.

    En fait on est dans le même cas que dans le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    std::unique_ptr<Base> foo() {
        std::unique_ptr<Derived> ptr(new Derived);
        //std::unique_ptr<Base> ptrbase(ptr); // erreur ici, ptr est une lvale value
        std::unique_ptr<Base> ptrbase(std::move(ptr)); // ok, std::move(ptr) est une rvalue
        return ptrbase;
    }
    Ceci-dit je soupçonne très fortement que la véritable intention est de pouvoir utiliser le polymorphisme Base/Dérivé et dans ce cas l'utilisation correcte est en fait la suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::unique_ptr<Base> foo() 
    {
        std::unique_ptr<Base> ptr(new Derived);
        //...
        return ptr;
    }
    Merci de l'explication !

    Il me reste toutefois une question, pour éclaircir.
    Etant donné que le return contiendrait donc un cast, comme return std::unique_ptr<Base>(ptr);, pourquoi le compilateur ne transforme-t-il pas ptr en xvalue, et donc n'appelle-t-il pas unique_ptr<T>(unique_ptr<U> &&) ?

    Par ailleurs, c'est bien pour faire du polymorphisme. Mais le code entre la déclaration et le return dois faire appel à une fonction spécifique de Derived. Donc je ne peux pas utiliser un unique_ptr<Base>.

    Citation Envoyé par Klaim Voir le message
    Si la fonction ne fait vraiment que ça (ou équivalent), l'écrire en une ligne permet au compilateur d'être sur que c'est un objet "temporaire" et va donc utiliser le move-constructor automatiquement à la place du constructeur par copie (si je ne me trompe pas, j'ai pas vérifié en compilant) :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    std::unique_ptr<Base> foo() 
    {
        return std::unique_ptr<Base> ptr(new Derived);
    }
    Mon code est en fait plus complexe que ça, évidemment.

  6. #6
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    La catégorie d'une expression ne change pas, ptr est une lvalue et restera une lvalue n'importe où dans ta fonction. Raison pour laquelle on utilise std::move(ptr) qui sera cette fois une xvalue (ptr reste une lvalue). Dans un return, le compilateur cast l'expression e à retourner vers le type de retour si son type n'est pas le bon, or le cast vers un class-type T n'est valide que si T f(e); est valide, ce qui n'est pas ton cas.

  7. #7
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut
    Merci !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [servlet][bean] return d'une variable
    Par LoLoSS dans le forum Servlets/JSP
    Réponses: 2
    Dernier message: 07/05/2004, 16h59
  2. [pl/pgSQL 7.2] Returns Opaque?
    Par Gruik dans le forum PostgreSQL
    Réponses: 1
    Dernier message: 18/10/2003, 16h50
  3. return ();exit() ;
    Par Zazeglu dans le forum C
    Réponses: 12
    Dernier message: 10/10/2003, 20h56
  4. [C#] Changer le focus avec la touche return
    Par sblanc74 dans le forum Windows Forms
    Réponses: 6
    Dernier message: 01/10/2003, 18h38
  5. return
    Par drKzs dans le forum C
    Réponses: 3
    Dernier message: 18/09/2003, 22h36

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo