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 :

rvalue vs lvalue


Sujet :

Langage C++

  1. #1
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut rvalue vs lvalue
    Bonjour tout le monde, je suis en train de lire le livre "Effective Modern c++" de Scott Meyers et je me rends compte qu'il me manque des connaissances de base concernant les rvalue et lvalue.

    J'aimerais donc savoir si j'ai bien compris:
    Une rvalue n'est pas modifiable contrairement à une lvalue.

    on ne pas écrire:

    En c++14 on peut passer les rvalue en paramètre, par exemple si on prend cette fonction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void foo(int && number);
    number est une rvalue.

    1) pourquoi ce code compile?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void foo(int&& number)
    {
      number = 5;
    }
    2) Ensuite, quel est l'intérêt (via std::move) de transformer une lvalue en rvalue? pourquoi ne pas faire une fonction qui prend en paramètre une lvalue?

    3) Je n'arrive pas à comprendre la différence entre copy-construct et move-construct:

    Soit deux constructeur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    widget(const int &a):_A(a) {}
    widget(int &&a): _A(std::move(a)) {}
    Je ne comprends pas la différence entre ces deux constructeur sachant que le move ne fait pas de "move" mais uniquement un cast en rvalue.

    Merci d'avance . J'aurais surement d'autres questions, mais qui se base sur ces trois premières questions.
    Pas de solution, pas de probleme

    Une réponse utile (ou +1) ->
    Une réponse inutile ou pas d'accord -> et expliquer pourquoi
    Une réponse à votre question


  2. #2
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Citation Envoyé par skeud Voir le message
    Une rvalue n'est pas modifiable contrairement à une lvalue.

    on ne pas écrire:
    Non, pas forcement. Par exemple :

    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int& f();
    f() = 5; // ok

    "f()" est une rvalue, mais est "modifiable". (En fait, c'est la référence - qui est une lvalue - qui sera modifiee). EDIT: cf plus loin dans la discussion.

    Citation Envoyé par skeud Voir le message
    En c++14 on peut passer les rvalue en paramètre, par exemple si on prend cette fonction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void foo(int && number);
    number est une rvalue.
    Petite correction : "on peut passer des lvalue et rvalue en arguments de fonction, et utiliser des lvalue references et des rvalue references dans des parametres de fonction".

    "number" est une lvalue de type "reference sur une rvalue". (Ne pas confondre la categorie de value : lvalue, rvalue, xvalue, glvalue, etc. et le type d'une value : lvalue reference, rvalue reference, int, double, etc.)

    Citation Envoyé par skeud Voir le message
    1) pourquoi ce code compile?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void foo(int&& number)
    {
      number = 5;
    }
    Parce que number est une lvalue non const (de type reference sur rvalue). Donc "assignable".

    Citation Envoyé par skeud Voir le message
    2) Ensuite, quel est l'intérêt (via std::move) de transformer une lvalue en rvalue? pourquoi ne pas faire une fonction qui prend en paramètre une lvalue?
    Correction : "transformer une lvalue reference en rvalue reference".

    La surcharge de fonction avec lvalue reference et rvalue reference permet d'écrire du code qui aura un comportement different en fonction de si l'argument est une lvalue ou une rvalue.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // on veut une fonction qui sera appellee indifféremment pour les lvalues et le rvalue:
    void f(int); // ou const& si la copie est lourde
    f(i); // ok, lvalue
    f(123); // ok, rvalue 
     
    // on veut des fonctions qui sera appellee différemment pour les lvalues et le rvalue:
    void g(int&); // #1
    void g(int&&); // #2
    g(i); // ok, lvalue avec #1
    g(123); // ok, rvalue avec #2
    Citation Envoyé par skeud Voir le message
    3) Je n'arrive pas à comprendre la différence entre copy-construct et move-construct:

    Soit deux constructeur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    widget(const int &a):_A(a) {}
    widget(int &&a): _A(std::move(a)) {}
    Je ne comprends pas la différence entre ces deux constructeur sachant que le move ne fait pas de "move" mais uniquement un cast en rvalue..
    Avec les infos precedentes, ca devrait etre plus clair.

    - c'est une surcharge de fonction, l'un des constructeurs sera appelle si l'argument est une lvalue, l'autre constructeur sera appelle si l'argument est une rvalue.

    - a est une lvalue dans les 2 constructeurs. Mais dans le second, il correspond a un objet temporaire. Sans le std::move, le constructeur de A sera appelé sur une lvalue (donc, dans A, sera copie plutot que deplace). Avec std::move, le constructeur de _A appelé est celui correspondant a une rvalue reference (et donc sera deplace plutot que copie, dans A).

  3. #3
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Non, pas forcement. Par exemple :

    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int& f();
    f() = 5; // ok

    "f()" est une rvalue, mais est "modifiable". (En fait, c'est la référence - qui est une lvalue - qui sera modifiee).
    Permet moi de ne pas être d'accord avec toi, pour moi ce n'est pas f() que l'on modifie mais le retour de f (autrement dis l'objet référencé par le retour de f).
    f retourne une référence sur un objet, c'est donc une lvalue et c'est cette lvalue que l'on modifie. Il est donc normal que ce code fonctionne car on assigne 5 à la lvalue retourner par f.
    En revanche si on prend ce code:
    ça ne fonctionne pas car f retourne une rvalue pour ce coup.


    Citation Envoyé par mintho carmo Voir le message
    Petite correction : "on peut passer des lvalue et rvalue en arguments de fonction, et utiliser des lvalue references et des rvalue references dans des parametres de fonction".

    "number" est une lvalue de type "reference sur une rvalue". (Ne pas confondre la categorie de value : lvalue, rvalue, xvalue, glvalue, etc. et le type d'une value : lvalue reference, rvalue reference, int, double, etc.)



    Parce que number est une lvalue non const (de type reference sur rvalue). Donc "assignable".
    Donc si je comprends bien, lorsque l'on fait:
    En réalité, on ne copie pas la valeur 5 dans number, mais on change la référence vers la rvalue 5?
    un peu le même principe que le code suivant?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void foo(int* p)
    {
       int n = 5;
       p = &n;
    }
    On ne modifie pas la valeur pointer par p mais l'endroit pointé par p.
    Donc number ne "pointe" plus sur la rvalue passée en argument mais sur la rvalue définis dans la ligne?
    Dans ce cas quelle est la portée des rvalue?


    Citation Envoyé par mintho carmo Voir le message
    Correction : "transformer une lvalue reference en rvalue reference".

    La surcharge de fonction avec lvalue reference et rvalue reference permet d'écrire du code qui aura un comportement different en fonction de si l'argument est une lvalue ou une rvalue.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // on veut une fonction qui sera appellee indifféremment pour les lvalues et le rvalue:
    void f(int); // ou const& si la copie est lourde
    f(i); // ok, lvalue
    f(123); // ok, rvalue 
     
    // on veut des fonctions qui sera appellee différemment pour les lvalues et le rvalue:
    void g(int&); // #1
    void g(int&&); // #2
    g(i); // ok, lvalue avec #1
    g(123); // ok, rvalue avec #2
    C'est à peu près l’intérêt que j'avais compris .


    Citation Envoyé par mintho carmo Voir le message
    - a est une lvalue dans les 2 constructeurs. Mais dans le second, il correspond a un objet temporaire. Sans le std::move, le constructeur de A sera appelé sur une lvalue (donc, dans A, sera copie plutot que deplace). Avec std::move, le constructeur de _A appelé est celui correspondant a une rvalue reference (et donc sera deplace plutot que copie, dans A).
    Qu'entends tout le monde par "déplacer", j'ai du mal à saisir la subtilité de ce mot dans ce contexte. Pour moi déplacer veut dire que la mémoire qui était identifié par la référence se retrouve dans la seconde référence, mais apparemment "move" ne fait qu'un cast et non pas de déplacement. C'est pourquoi je comprends pas pourquoi on parle de "déplacer dans A".

    Celà veut-il dire que l'argument "a" ne contiendra plus rien? Comment peut-on avoir une référence sur rien?
    Pas de solution, pas de probleme

    Une réponse utile (ou +1) ->
    Une réponse inutile ou pas d'accord -> et expliquer pourquoi
    Une réponse à votre question


  4. #4
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Citation Envoyé par skeud Voir le message
    Permet moi de ne pas être d'accord avec toi, pour moi ce n'est pas f() que l'on modifie mais le retour de f (autrement dis l'objet référencé par le retour de f).
    f retourne une référence sur un objet, c'est donc une lvalue et c'est cette lvalue que l'on modifie. Il est donc normal que ce code fonctionne car on assigne 5 à la lvalue retourner par f.
    Dans l'expression "f() = 5;", "f()" est bien une rvalue (plus précisément, si j'ai bien compris la doc, une xvalue). Quelque soit le type de cette value (attention categorie vs type).

    Dire que "Une rvalue n'est pas modifiable contrairement à une lvalue", c'est un trop gros raccourci, a mon avis. (Idem d'ailleurs dans l'autre sens : un lvalue n'est pas forcement modifiable).

    Plus généralement, la catégorie de value ne dit rien sur la "modifiabilité" ou non de cette value. C'est le type de la value qui determine cela.
    EDIT: cf plus loin dans la discussion.

    Citation Envoyé par skeud Voir le message
    Donc si je comprends bien, lorsque l'on fait:
    En réalité, on ne copie pas la valeur 5 dans number, mais on change la référence vers la rvalue 5?
    un peu le même principe que le code suivant?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void foo(int* p)
    {
       int n = 5;
       p = &n;
    }
    On ne modifie pas la valeur pointer par p mais l'endroit pointé par p.
    Donc number ne "pointe" plus sur la rvalue passée en argument mais sur la rvalue définis dans la ligne?
    Euh... non. (ou alors je comprends pas ce que tu racontes).
    Pourquoi la référence serait changée ?

    Citation Envoyé par skeud Voir le message
    Dans ce cas quelle est la portée des rvalue?
    Par définition, une rvalue n'a aucune portée.

    Citation Envoyé par skeud Voir le message
    Qu'entends tout le monde par "déplacer", j'ai du mal à saisir la subtilité de ce mot dans ce contexte. Pour moi déplacer veut dire que la mémoire qui était identifié par la référence se retrouve dans la seconde référence, mais apparemment "move" ne fait qu'un cast et non pas de déplacement. C'est pourquoi je comprends pas pourquoi on parle de "déplacer dans A".
    "deplacer" est un terme impropre pour dire que la value ne sera plus utilisée par la suite et donc que le compilateur peut faire ce qu'il veut avec cette value (ou presque). En particulier, il n'est pas oblige de conserver cette value dans son état initial.

    Par exemple, si on copie un std::string, il faut que la std::string initiale ne soit pas modifiee. Pour cela, qu'une méthode : il faut copier le tableau de char interne.
    Si on "deplace" un std::string, le tableau de char interne ne sera plus utilise par la suite dans la std::string initiale. Le compilateur peut donc "voler" directement les données de la premier std::string pour creer la seconde.

    Citation Envoyé par skeud Voir le message
    C'est pourquoi je comprends pas pourquoi on parle de "déplacer dans A".
    Tu as 2 appels de fonction : l'appel du constructeur de widget et l'appel du constructeur de A.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    A::A(...) ... 
     
    widget::widget(...) ... // #1
     
    widget(...); // #2
    Donc 4 "chemins" possible pour les appels de fonctions surchargées :
    - widget::widget(A&) puis A::A(A&)
    - widget::widget(A&) puis A::A(A&&)
    - widget::widget(A&&) puis A::A(A&)
    - widget::widget(A&&) puis A::A(A&&)

    Dans tous les cas, la résolution de la surcharge des fonctions est déterminée par la catégorie de l'argument lors de l'appel.
    Pour le constructeur de widget, c'est donc l'argument donné a la ligne #2 :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    A a;
    widget(...); // appel de widget(A&)
     
    widget(A()); // appel de widget(A&&)
    Pour le constructeur de A, cela se passe a la ligne #1.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    widget(A& a):A(a) {}
    widget(A&& a):A(a) {}
    Dans ce code, pour les 2 lignes, "a" est une lvalue. Donc dans les 2 cas, c'est le constructeur A::A(A&) qui est appelé. Donc, dans widget(A&&), "a" sera copie dans le constructeur de A, plutot que deplace.

    Pour eviter cela et appeler le constructeur par déplacement de A, il faut "forcer" l'appel de A(A&&), en convertissant la lvalue "a" en rvalue. C'est ce que fait std::move.

    Citation Envoyé par skeud Voir le message
    Celà veut-il dire que l'argument "a" ne contiendra plus rien? Comment peut-on avoir une référence sur rien?
    Non, "a" doit etre un objet valide, mais on ne connait pas son etat.

  5. #5
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 460
    Points : 6 064
    Points
    6 064
    Par défaut
    Bonjour,

    skeud, voici un article assez bien fait, concis et pédagogique, qui répond brièvement à toutes tes questions :
    http://www.artima.com/cppsource/rvalue.html

    Cependant, j'ajoute deux remarques à propos de cet article :
    • Dans leur exemple de code de clone_ptr, l'affectation de copie n'est pas exception safe. Ils auraient dû cloner d'abord et faire le delete ensuite.
    • La dernière partie, sur le perfect forwarding, va au delà de tes questions que départ et passe sous silence certaines subtilités. Si tu la lis puis que tu désires approfondir ces subtilités, voici un 2e lien :
      http://stackoverflow.com/questions/3...582313#3582313

  6. #6
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 562
    Points : 7 628
    Points
    7 628
    Par défaut
    Bonjour,

    On voit que ce n'est pas si simple ces concepts!
    Je me permets de re-répondre car certaines réponses me paraissent trompeuses voire erronées
    Citation Envoyé par skeud Voir le message
    Bonjour tout le monde, je suis en train de lire le livre "Effective Modern c++" de Scott Meyers et je me rends compte qu'il me manque des connaissances de base concernant les rvalue et lvalue.
    Je t'encourage surtout à bien lire les explications de Scott Meyers qui sait bien de quoi il parle. Et de lire les deux raccourcis fournis par pyramidev.
    Citation Envoyé par skeud
    pourquoi ce code compile?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void foo(int&& number)
    {
      number = 5;
    }
    number a reçu une référence sur une rvalue, il est lui-même une lvalue (tout ce qui a un nom est une lvalue).
    Citation Envoyé par skeud
    Ensuite, quel est l'intérêt (via std::move) de transformer une lvalue en rvalue? pourquoi ne pas faire une fonction qui prend en paramètre une lvalue?
    Quand une fonction reçoit une r-référence, elle sait que c'est soit une xvalue(c-a-d le move d'une lvalue), soit une pr-value(c-a-d une temporaire), elle peut en extraire des composants, après cela la variable n'est plus viable pour être utilisée.
    Le move ne doit se faire que sur la dernière utilisation d'une variable qui sera 'dépecée'.
    exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void fct(std::vector<int>&&);
    std::vector<double> v{1,2,3,4,5};
    fct( std::vector<int>{2,3,4} ); // ici c'est un temporaire, la fonction va se servir dans le vector et le modifiera si ça lui chante
    fct( std::move(v) ); // Le vector et son contenu est transféré à la fonction, on a indiqué que l'on a plus besoin de v
    v.push_back(4);      // ça compile mais c'est interdit, la variable a été utilisée dans un move, elle n'est plus viable
    Citation Envoyé par skeud
    Je n'arrive pas à comprendre la différence entre copy-construct et move-construct:
    Je ne comprends pas la différence entre ces deux constructeur sachant que le move ne fait pas de "move" mais uniquement un cast en rvalue.
    Si on construit l'objet à partir d'un temporaire ou d'un move, on utilisera le move-constr qui pourra optimiser en se servant dans le paramètre. Mais si on construit à partir d'une lvalue, le paramètre devra être préservé. Le move-constr permet donc quand cela a un sens de mieux optimiser; il n'a d’intérêt que si le paramètre est un objet complexe.

  7. #7
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Citation Envoyé par dalfab Voir le message
    Je me permets de re-répondre car certaines réponses me paraissent trompeuses voire erronées.
    Comme j'imagine que tu parles de mes réponses, tu pourrais preciser ? (Surtout que tu dis en gros la meme chose que moi)

  8. #8
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 460
    Points : 6 064
    Points
    6 064
    Par défaut
    Citation Envoyé par dalfab Voir le message
    Le move ne doit se faire que sur la dernière utilisation d'une variable qui sera 'dépecée'.
    exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void fct(std::vector<int>&&);
    std::vector<double> v{1,2,3,4,5};
    fct( std::vector<int>{2,3,4} ); // ici c'est un temporaire, la fonction va se servir dans le vector et le modifiera si ça lui chante
    fct( std::move(v) ); // Le vector et son contenu est transféré à la fonction, on a indiqué que l'on a plus besoin de v
    v.push_back(4);      // ça compile mais c'est interdit, la variable a été utilisée dans un move, elle n'est plus viable
    Pour nuancer ce que dit dalfab :
    • Si on documente dans quel état le vector sera après l'appel de ftc, alors le vector sera toujours utilisable.
      Un exemple analogue est le constructeur de mouvement de std::unique_ptr :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      void voler(std::unique_ptr<int>& param) {
      	std::unique_ptr<int> voleur(std::move(param));
      	assert(param.get() == nullptr); // L'état de param est connu ici.
      	param.reset(new int(5)); // On peut utiliser param.
      	// ...
      }
    • Le standard dit ceci à propos des types de la STL :
      17.6.5.15 Moved-from state of library types

      Objects of types defined in the C++ standard library may be moved from (12.8). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
      Citation pompée ici : http://stackoverflow.com/questions/1...95473#12095473
      On a alors :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      void toto() {
      	std::vector<int> v1{1,2,3};
      	std::vector<int> v2{8,9};
      	v1 = std::move(v2); // v2 est alors dans un état indéfini, mais valide.
      	v2.push_back(4); // Fonctionne, mais on ne sait toujours pas dans quel état est v2.
      	v2 = v1; // Maintenant, l'état de v2 est de nouveau connu.
      }


    EDIT :

    Citation Envoyé par mintho carmo Voir le message
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int& f();
    f() = 5; // ok

    "f()" est une rvalue, mais est "modifiable". (En fait, c'est la référence - qui est une lvalue - qui sera modifiee).
    Citation Envoyé par mintho carmo Voir le message
    Dans l'expression "f() = 5;", "f()" est bien une rvalue (plus précisément, si j'ai bien compris la doc, une xvalue).
    Non, f() est une lvalue :
    The following expressions are lvalue expressions:
    • [...]
    • a function call or an overloaded operator expression of lvalue reference return type, such as std::getline(std::cin, str), std::cout << 1, str1 = str2, or ++it;
    Source : http://en.cppreference.com/w/cpp/lan...value_category

  9. #9
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    Effectivement, j'ai été trop vite sur ce coup là.
    Ok, donc "rvalue = non modifiable" n'est pas un raccourci.
    (Par contre, penser que c'est ce qui caractérise une rvalue, cela ne doit pas aider à la compréhension du fonctionnement des rvalue/lvalue ref. Je vois plus cela comme une conséquence)

  10. #10
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Impec, merci pour le liens, j'ai bien compris le principe de rvalue et lvalue du coup .

    Concernant les xvalue, glvalue ..... c'est du c++17 du coup je ne m'en préoccupe pas pour le moment . (on ne va pas compliqué les choses plus qu'elle ne le sont déjà).

    Ce que je ne comprenais pas avec le move, c'est pourquoi il déplaçait?
    J'ai compris de cette manière:

    Le move ne déplace rien du tout, il transforme juste le paramètre en rvalue (donc à ne plus utiliser après l'appel du move).
    En revanche c'est la fonction appelée, qui reçoit une rvalue et qui peut donc en disposé comme elle veut, c'est cette fonction qui décidera si elle veut déplacer ou non la rvalue reçu.
    Donc move = passage en rvalue.
    Et fonction qui reçoit une rvalue = elle en fait ce qu'elle veut.
    Comme on ne sait pas ce que fera cette fonction, on considère que l'état d'une variable après le passage dans un move est indéfinis.
    Si la fonction appelé n'effectue pas le déplacement, il n'y en aura pas.

    Je passe le sujet en résolu, mais je n'hésiterais pas à revenir si j'ai d'autres questions. Merci à tous .
    Pas de solution, pas de probleme

    Une réponse utile (ou +1) ->
    Une réponse inutile ou pas d'accord -> et expliquer pourquoi
    Une réponse à votre question


  11. #11
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Citation Envoyé par skeud Voir le message
    Concernant les xvalue, glvalue ..... c'est du c++17 du coup je ne m'en préoccupe pas pour le moment . (on ne va pas compliqué les choses plus qu'elle ne le sont déjà).
    C'est du C++11 (cf le lien donné par Pyramidev, la partie History). Mais c'est du détail sémantique.

  12. #12
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    C'est du C++11 (cf le lien donné par Pyramidev, la partie History). Mais c'est du détail sémantique.
    Au temps pour moi, j'avais mal lu cet article (quand j'ai vu "since c++17" j'ai compris que ça apparaissait avec le c++17 et non pas que c'était considéré comme tel depuis c++17).
    Pas de solution, pas de probleme

    Une réponse utile (ou +1) ->
    Une réponse inutile ou pas d'accord -> et expliquer pourquoi
    Une réponse à votre question


  13. #13
    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
    Pour moi, le move ne transforme pas en rvalue, mais en rvalue reference. Ce qui permet de le passer à des fonctions ou constructeurs susceptibles de faire un déplacement.
    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.

  14. #14
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 186
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 186
    Points : 17 126
    Points
    17 126
    Par défaut
    Au passage, j'avais trouvé les explications sur cppreference assez explicites.

    Notamment la première partie.
    * a glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function
    * a prvalue is an expression whose evaluation either
    - computes the value of the operand of an operator (such prvalue has no result object), or
    - initializes an object or a bit-field (such prvalue is said to have a result object). All class and array prvalues have a result object even if it is discarded.

    * an xvalue is a glvalue that denotes an object or bit-field whose resources can be reused
    * an lvalue is a glvalue that is not an xvalue.
    * an rvalue is a prvalue or an xvalue.
    J'invite à lire la page complète.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  15. #15
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 460
    Points : 6 064
    Points
    6 064
    Par défaut
    Citation Envoyé par skeud Voir le message
    Une rvalue n'est pas modifiable contrairement à une lvalue.
    Citation Envoyé par mintho carmo Voir le message
    Ok, donc "rvalue = non modifiable" n'est pas un raccourci.
    (Par contre, penser que c'est ce qui caractérise une rvalue, cela ne doit pas aider à la compréhension du fonctionnement des rvalue/lvalue ref. Je vois plus cela comme une conséquence)
    Encore raté. Une rvalue peut être modifiée si c'est une classe non const.
    Exemple :
    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
    #include <string>
     
    class Personne
    {
    private:
    	std::string m_nom;
    	int         m_age;
     
    	explicit Personne(int age) : m_age(age) {}
     
    public:
    	inline static Personne creerJeuneAdulte() { return Personne(18); } // retourne une prvalue (donc une rvalue)
    	Personne& setNom(const std::string& nom) { m_nom = nom; return *this; } // modifie l'objet
    	int getAge() const { return m_age; } // retourne une prvalue (donc une rvalue)
    };
     
    int main()
    {
    	Personne alain = Personne::creerJeuneAdulte().setNom("Alain");
    		// Ça compile, car le type de retour de "creerJeuneAdulte" est une classe non const.
     
    //	int ageDAlainDansUnAn = ++(alain.getAge()); // Erreur de compilation
    		// Ça ne compile pas, car le type de retour de "getAge" n'est pas une classe.
     
    	return 0;
    }
    D'un point de vue pratique, ce qui caractérise une rvalue, c'est surtout qu'on peut l'utiliser pour initialiser une rvalue reference.

  16. #16
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    J'aurais du rester avec ma première pensée Je n'ai pas trop réfléchi a des exemples de rvalue modifiable, mais de toute façon, cela me semblais bancale de penser en termes de "rvalue = non modifiable".

    Bon, le plus important a mon sens, c'était de corriger cette erreur (classique) de confondre catégorie et type d'une value. Beaucoup pensent que dans "int&& number", number est une rvalue et ne comprennent pas l'intérêt du std::move.

  17. #17
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    @Pyramidev, tes commentaires ne sont pas assez précis (et rien n'empêche d'appeler une fonction libre au lieu d'une fonction membre) :
    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
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -Ofast -std=c++14 -pedantic -fopenmp main.cpp -o main && ./main
     
    #include <string>
     
    class Personne
    {
    private:
     
    	std::string m_nom;
    	int         m_age;
     
    	explicit Personne(int age) : m_age(age) {}
     
    public:
     
    	static Personne creerJeuneAdulte() { return Personne(18); }
     
    	Personne& setNom(const std::string& nom) { m_nom = nom; return *this; }
     
    	int & getAge() { return m_age; }
    };
     
    int main()
    {
    	Personne alain = Personne::creerJeuneAdulte().setNom("Alain"); // Ok
    	int ageDAlainDansUnAn = ++(alain.getAge()); // Ok
     
    	return 0;
    }
    À titre personnel, je trouve que les discussions sur les modifications éventuelles des rvalues sont un peu bancales.
    Car, en fait pour les modifier, on appelle une fonction. Et dans cette fonction, la rvalue devient une lvalue (elle a un nom).
    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
    // g++ -Wall -Wextra -Wconversion -Wsign-conversion -Ofast -std=c++14 -pedantic -fopenmp main.cpp -o main && ./main
     
    #include <iostream>
     
    void f(int && i) // capture par rvalue
    {
    	++i; // i est une lvalue (localement (dans la fonction) "i" a un nom)
    	std::cout << i << std::endl;
    }
     
    int main()
    {
    	f(7); // 7 est une rvalue
     
    	return 0;
    }

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 07/08/2014, 15h35
  2. lvalue rvalue ?
    Par FoX_*D i E* dans le forum Débuter
    Réponses: 4
    Dernier message: 23/12/2012, 12h41
  3. rvalue reference et lvalue
    Par guillaume07 dans le forum C++
    Réponses: 4
    Dernier message: 08/12/2010, 13h17
  4. Rvalues et Lvalues
    Par deubelte dans le forum Débuter
    Réponses: 1
    Dernier message: 29/04/2010, 17h36

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