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

Langage C++ Discussion :

[c++0x] Lumière sur les rvalue references


Sujet :

Langage C++

  1. #1
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut [c++0x] Lumière sur les rvalue references
    Bonjour!

    J'ai accès depuis quelques jours à une config Linux, et j'en profite pour tester le mode experimental c++0x de gcc. J'essaie de comprendre les rvalue references et l'impact qu'elles auront sur notre manière de coder, mais je bute sur deux problèmes :

    Question 1

    Première essai. Une classe "movable" mais pas copiable
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    struct NonCopyable
    {
        NonCopyable() = default;
        NonCopyable(NonCopyable&&) = default;
        //empeche la copie
        NonCopyable(const NonCopyable&) = delete;
        NonCopyable& operator=(const NonCopyable&) = delete;
    };
    Mais ça ne compile pas. Gcc m'annonce qu'il est impossible d'avoir un move contructeur par défaut. Pourquoi cela ?
    Ne pourrait pas avoir un move constructeur qui ferait des move membre à membre ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    NonCopyable(NonCopyable&& ncp):
    membre1(move(ncp.membre1),
    membre2(move(ncp.membre2), 
    ...
    Question 2

    A l'heure actuelle, pour appliquer un traitement sur un objet lourd, la syntaxe revient toujours plus ou moins à foo(HeavyClass&, Param1, Param2, Param3...). Esthétiquement je préfère de beaucoup la syntaxe HeavyClass foo(Param1, Param2, Param3)... mais il y a la copie.

    J'avais cru comprendre que les rvalue references allaient réunir les deux mondes et nous permettre ce genre de chose :
    X&& foo();
    X x = foo();
    Sans copie aucune. \0/

    Ben il semble que non.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    std::vector<std::string>&& parse(const std::string& s, char token)
    {
        std::vector<std::string> result;
        std::string::size_type first = 0, last;
        while (first != std::string::npos)
        {
            last = s.find_first_of(token, first);
    	result.push_back(s.substr(first, last - first));
    	first = s.find_first_not_of(token, last);
        }
        return move(result); // move explicite
    }
    std::vector<std::string> parse = foo("Le.c++0x.c'est.l'avenir.",'.');
    Segmentation fault.
    Le mode debug confirme que le destructeur de result est appelé en sortant du scope de parse(), avant le move constructeur, d'où la segmentation fault. Or je croyais que le rôle même de std::move était de prolonger un peu les temporaires pour leur donner le temps de faire les opérations impliquant les rvalue reference et seulement ensuite d'être détruit. Ou cela coince-t-il ?

    Merci!

  2. #2
    Membre actif
    Profil pro
    Inscrit en
    Août 2007
    Messages
    190
    Détails du profil
    Informations personnelles :
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Août 2007
    Messages : 190
    Points : 219
    Points
    219
    Par défaut
    Salut,

    Juste une remarque concernant dans ton précédent message, un code de ce type:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class X { /*...*/ };
     
    X foo();
     
    int main()
    {
      X x=foo();
     
      return 0;
    }
    ne va pas générer une copie* comme tu as l'air de le croire.

    * à condition bien entendu d'avoir un bon compilateur qui utilise le NRVO

  3. #3
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Ce n'est pas std::vector<std::string>&& parse(const std::string& s, char token)
    mais std::vector<std::string> parse(const std::string& s, char token).
    Et il n'y a pas besoin de move explicite.

    Par contre, je ne suis pas certain que la bibliothèque standard de GCC soit move-aware...

    Il faut retourner par valeur, sinon tu retournes une réference vers un temporaire...
    Boost ftw

  4. #4
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Les rvalue references et la move semantics, tout ceci n'est que de la… sémantique ! Autrement dit, cela ne sert qu'à exprimer plus précisément ce que tu attends de ton code.

    En outre, la fonction std::move() n'a rien de magique (elle ne prolonge en aucun cas la durée de vie d'une variable). Elle ne sert qu'à préciser que tu veux te servir d'une variable en tant que rvalue reference (en bref, c'est équivalent à un static_cast). Voici par ailleurs sa définition, qui est très simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    template <class T>
    typename remove_reference<T>::type&&
    move(T&& a)
    {
        return a;
    }
    Là où c'est intéressant, c'est que cette sémantique te permet de préciser ce que tu veux qu'il se passe quand un objet est envoyé en tant que rvalue reference en paramètre d'une fonction. Par exemple un constructeur (ce sera alors ce qu'on appelle un move constructor). Lorsque tu utilises un move constructor, tu exprimes le fait que l'objet qui est passé en paramètre peut être altéré, vidé, réinitialisé… ça t'est complètement égal, du moment que l'objet construit soit au final égal à l'objet passé en paramètre (ou plutôt sa valeur avant que celui-ci soit altéré, bien sûr).
    Mais un move constructor n'a rien de magique. Les instructions que contiennent ce type de constructeur ne font rien figurer de nouveau qu'on ne connaissait pas en C++98.

    Voici l'exemple le plus parlant : mettons que tu aies une classe clone_ptr, qui contient un pointeur vers une donnée (peu importe le type de cette donnée).
    Dans un copy constructor classique, cette donnée devra être copiée (et ça peut être long, selon la taille de la donnée en question).
    Alors que dans un move constructor, on va se contenter de copier le pointeur qui pointe vers cette donnée. D'autre part, on va réinitialiser la valeur du pointeur de l'objet (passé en paramètre du constructeur) à zéro, histoire qu'il n'y ait pas de conflit. Deux affectations de int, et c'est réglé : c'est donc extrêmement rapide. L'inconvénient, c'est que l'objet passé en paramètre du constructeur a été altéré, mais pas de problème, puisque tu as clairement exprimé le fait que ça t'était égal.
    Voici le code de cette fameuse classe clone_ptr :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    template <class T>
    class clone_ptr
    {
    private:
        T* ptr;
    public:
        // construction
        explicit clone_ptr(T* p = 0) : ptr(p) {}
     
        // destruction
        ~clone_ptr() {delete ptr;}
     
        // copy semantics
        clone_ptr(const clone_ptr& p)
            : ptr(p.ptr ? p.ptr->clone() : 0) {}
     
        clone_ptr& operator=(const clone_ptr& p)
        {
            if (this != &p)
            {
                delete ptr;
                ptr = p.ptr ? p.ptr->clone() : 0;
            }
            return *this;
        }
     
        // move semantics
        clone_ptr(clone_ptr&& p)
            : ptr(p.ptr) {p.ptr = 0;}
     
        clone_ptr& operator=(clone_ptr&& p)
        {
            std::swap(ptr, p.ptr);
            return *this;
        }
     
        // Other operations
        T& operator*() const {return *ptr;}
        // ...
    };
    Source : http://www.artima.com/cppsource/rvalue.html

    Maintenant, dans un cas concret d'une classe dont les attributs sont des variables de type primaire et des conteneurs de la STL, tu n'auras pas à faire ce type d'opérations de pointeurs, puisque les classes de la lib standard de C++0x définiront des move constructors et des move assignment operator (surcharge d'operator= prenant une rvalue reference). Tu feras donc un move de ta string ou de ton vector, et la lib standard fera ce qu'il faut.


    Sinon pour répondre directement à tes questions :
    1) Effectivement, tu peux retourner par valeur sans t'inquiéter, renseigne-toi sur la NRVO.
    2) Le comité ISO en cause ici : http://www.open-std.org/jtc1/sc22/wg...008/n2583.html
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  5. #5
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Ton operator=(const clone_ptr&) est mauvais.
    Que se passe-t-il si clone lève une exception ?
    Boost ftw

  6. #6
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Je te renvoie aux auteurs de l'article que j'ai cité, à savoir Howard E. Hinnant, Bjarne Stroustrup et Bronek Kozicki
    Je n'ai pas vraiment analysé l'aspect exception-safe de ce code, d'une part parce que ce sont les messieurs du dessus qui l'ont écrit et d'autre part parce que sa raison d'être est plus pédagogique qu'autre chose.
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  7. #7
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Ces messieurs ne pratiquent donc pas le vénéré "copy and swap" ?

  8. #8
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Apperemment pas... enfin dans ce code à visé didactique. Parce que sinon si j'ai déjà vu des papiers de BS et autres où ils recommandaient bien le copy'n'swap.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  9. #9
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Le copy-and-swap, dans le cas des rvalue references, ça consiste le plus souvent à faire juste le swap, non?
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Ça ne choque que moi de faire du "copy-and-swap" sur un "non-copyable" ?

  11. #11
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    clone_ptr n'est pas noncopyable.
    On parle de son affectation de copie, là.

    Pour les move semantics, un swap n'est pas forcément le meilleur choix puisque l'ancienne ressource ne sera libérée que tardivement.
    Boost ftw

  12. #12
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Merci pour toutes ces réponses. (spécialement à Florian, ton explication était bienvenue)
    Bon entre temps j'ai trouvé ça. L'histoire se répète.
    Citation Envoyé par Florian Goo
    Merci. Pour une raison ou une autre je l'avais manqué.
    Et donc, ce move constructeur par défaut :
    "Semantically, the default implementation of the move constructor is a recursive move construction of its subobjects. In the event that there is no move constructor defined for a subobject, the copy constructor will be used instead."
    La papier a été proposé en mars 2008, mais apparemment n'a pas encore été débattu aux réunions du comité.
    Citation Envoyé par Florian Goo
    1) Effectivement, tu peux retourner par valeur sans t'inquiéter, renseigne-toi sur la NRVO.
    Certes.
    Le problème c'est que la NRVO n'est qu'une optimisation laissé au bon vouloir du compilateur. Si, par exemple, mon objet de retour est un vecteur contenant deux millions d'int, je n'ai vraiment pas envie de voir l'optimisation disparaitre et se transformer en copie en mode debug, par exemple. (et la NRVO est désactivé en debug sous VS2008)

    Mais j'ai finalement trouvé comment forcer le move constructeur. Comme indiqué par loufoque la bonne syntaxe est la suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    A foo();
    A a = move(foo());
    Avec dans l'ordre des appels :
    Constructeur de A (quelque part dans la fonction foo)
    Move constructeur
    Destructeur du temporaire
    C'est un peu moins efficace que la NRVO, qui n'appelle effectivement qu'un seul constructeur, mais au moins le comportement est assuré.
    Citation Envoyé par loufoque
    Pour les move semantics, un swap n'est pas forcément le meilleur choix puisque l'ancienne ressource ne sera libérée que tardivement.
    Oui, c'est le problème répertorié ici

    Ils proposent de remplacer les swap des move assignement par des clear+swap. Donc, le code d'artima deviendrait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    clone_ptr& operator=(clone_ptr&& p)
    {
       delete(ptr); // Ressource libérée tout de suite
       std::swap(ptr, p.ptr);
       return *this;
    }

  13. #13
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Mais j'ai finalement trouvé comment forcer le move constructeur. Comme indiqué par loufoque la bonne syntaxe est la suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A foo();
    A a = move(foo());
    Avec dans l'ordre des appels :
    Constructeur de A (quelque part dans la fonction foo)
    Move constructeur
    Destructeur du temporaire
    C'est un peu moins efficace que la NRVO, qui n'appelle effectivement qu'un seul constructeur, mais au moins le comportement est assuré.
    Ne serait-ce pas plutôt
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A && foo();
    A a = foo();//move construct called if exist, otherwise copy construct or error ?
    Je ne verrais pas l'intérêt du nouvel opérateur && s'il faut se farcir un move explicit à chaque fois.

    Par ailleurs, serait-il faux d'avoir une rvalue constante ? (je dirais que oui)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void AnotherFoo(const A && a);

  14. #14
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Damned.
    Merci d'avoir fait remonter le sujet Camboui, car je l'avais complètement oublié et en effet :
    Mais j'ai finalement trouvé comment forcer le move constructeur. Comme indiqué par loufoque la bonne syntaxe est la suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Code :
    A foo();
    A a = move(foo());
    C'est une belle connerie

    Entre temps j'ai finalement compris que :
    1) GCC contrairement à MSVC active le NRVO même en debug. (le fourbe...) Il faut utiliser -fno-elide-constructors pour le désactiver.
    2) std::move se comporte effectivement comme un simple static_cast<T&&>, il ne faut donc l'utiliser que dans le cas ou l'on veut explicitement transformer une lvalue en rvalue.

    Donc au final, il ne faut pas faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    A foo();
    A a = move(foo());
    Ni
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    A && foo();
    A a = foo();
    Mais bien
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A foo();
    A a = foo();
    Car foo() renvoie soit un temporaire, donc une rvalue, si la NRVO est désactivé, soit construit sur place. Dans tous les cas c'est efficace.

    Bon, d'une manière plus générale, j'étais partis pour écrire un long laîus sur ce que j'avais finalement compris des rvalue reference, mais Stephan T. Lavavej (Développeur pour Visual C++ 10) vient de pulvériser ma misérable tentative.

    Ce type est génial, il a déjà écrit une longue explication des lambdas, particulièrement claire et pédagogique, et il vient de remettre le couvert, il y a à peine deux jours avec les values references. Il reprend quasiment les mêmes points que l'article d'artima sauf qu'il les développe sur 37 (!!) pages, pas à pas, avec un exemple clair et compréhensible à chaque étape: c'est le pied.

    Stephan T. Lavavej (alias S.T.L., ça me fera toujours rire)
    lambdas, auto, and static_assert Part 1
    Rvalue References: C++0x Features in VC10, Part 2

  15. #15
    Rédacteur
    Avatar de Bakura
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    1 386
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 386
    Points : 2 640
    Points
    2 640
    Par défaut
    Je me permets d'upper ce topic poru vous poser une petite question. Je trouve pas mal d'usage à ces rvalue, notamment lorsque j'alloue un vector assez gros, et que j'ai besoin de le garder uniquement dans un autre objet, c'est assez pratique car je peux l'allouer quelque part, et le "move" vers un autre objet.

    Par contre, quel genre de commentaire peut-on écrire pour signifier que l'on va effectuer un move. Par exemple :

    MonConstructor (std::vector<unsigned int> & donnees_);

    Quel type de commentaire pusi-je mettre pour prévenir que le tableau original dans la fonction appelante ne va plus être valide car je vais le bouger ? En théorie, je n'aurais pas besoin d'écrire un tel commentaire puisque ce tableau n'a pas vocation d'être réutilisé dans la fonction appelante, mais je trouve ça plus propre de le signaler...

  16. #16
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Attention toute fois que l'article sur le blog dev de VS10 n'est plus à jour. Les régles on changés pour les rvalues (et vont encore changé je crois). Enfin je sais pas ce que tu utilises après tout ^^.

    Sinon :


    /* move semantics */ ? xD
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  17. #17
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Citation Envoyé par Bakura Voir le message
    MonConstructor (std::vector<unsigned int> & donnees_);
    Je ne sais pas si c'est une faute d'inattention, mais c'est ça qu'il faut écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MonConstructor (std::vector<unsigned int> && donnees_);
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  18. #18
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Bonjour,
    Je n'ai pas très bien compris le système. Quelle est cette mystérieuse "fonction appellante" ? Qui veut déplacer quoi ?

    Pourrais-tu donner un bout de code pour fixer les idées ?

    Sinon, en général, le code s'auto-commente, car il est impossible de déclencher un déplacement d'une lvalue sans un std::move. Donc dès qu'on voit un std::move(qqchose) alors il faut considérer qu'après le move ce qqchose est dans un état indéterminé.

  19. #19
    Rédacteur
    Avatar de Bakura
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2005
    Messages
    1 386
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 386
    Points : 2 640
    Points
    2 640
    Par défaut
    Exemple :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void MaFonctionDuneClasseA ()
    {
        std::vector<unsigned int> vector;
     
        // Charge de nombreux entiers à partir d'un fichier, et met tout ça dans le vector
     
        // Mais ce vector n'a pas objectif d'être utilisé dans cette classe, juste chargé, alors je le passe à la classe correspondante
        monAutreObjet.Set (vector);
     
        // Vu que je n'ai plus de raison de l'utiliser ici, autant éviter la copie et le "bouger"
    }
    Je ne savais pas que c'était &&, je croyais qu'il suffisait de mettre &, et que le && ne servait que pour les valeurs de retours... Mais si effectivement il faut mettre &&, alors ça s'auto-commente .

  20. #20
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Il manque une petite chose, en fait il faut faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    MonAutreObjet::Set(std::vector<unsigned int>&& vec)
    {
    ...
    }
    
    void MaFonctionDuneClasseA ()
    {
        std::vector<unsigned int> vector;
     
        // Charge de nombreux entiers à partir d'un fichier, et met tout ça dans le vector
     
        // Mais ce vector n'a pas objectif d'être utilisé dans cette classe, juste chargé, alors je le passe à la classe correspondante
        monAutreObjet.Set (std::move(vector));
     
        // Vu que je n'ai plus de raison de l'utiliser ici, autant éviter la copie et le "bouger"
    }
    il faut appeller std::move pour forcer le déplacement, car vector est une lvalue (c-a-d une variable qui a un nom, dont on peut prendre l'adresse) donc normalement non déplacable.

    std::move permet de dire au compilateur : "je sais ce que je fait, même si vector est une lvalue, tu peux le considerer comme rvalue"
    (sd::move est presque équivalent à un static_cast<T&&>() en fait)

    C'est un filet de protection supplémentaire. Ca serait beaucoup trop confus et dangereux si un code d'apparence banal " monAutreObjet.Set(vector);" provoquait un déplacement uniquement parce que la fonction Set accepte des std::vector<>&&.

Discussions similaires

  1. Besoin de lumières sur les VBO et le texturing s.v.p.
    Par la_tupac dans le forum OpenGL
    Réponses: 17
    Dernier message: 31/08/2010, 18h34
  2. les rvalue reference
    Par yan dans le forum C++
    Réponses: 15
    Dernier message: 15/05/2008, 16h45
  3. Besoin de Lumière sur les Licences d'Accés Client
    Par taz61 dans le forum Windows Serveur
    Réponses: 0
    Dernier message: 26/09/2007, 14h35

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