Qu'est-ce que rvalues, lvalues, xvalues, glvalues, et prvalues?

En C++03, une expression est soit un rvalue ou un lvalue .

En C++11, une expression peut être une:

  1. rvalue
  2. lvalue
  3. value
  4. glvalue
  5. prvalue

deux catégories sont devenues cinq catégories.

  • Quelles sont ces nouvelles catégories d'expressions?
  • Comment ces nouvelles catégories se rapportent à l'existant rvalue et lvalue catégories?
  • les catégories rvalue et lvalue en C++0x sont-elles les mêmes qu'en C++03?
  • pourquoi ces nouvelles catégories sont-elles nécessaires? Sont le WG21 des dieux qui essaient de nous embrouiller, simples mortels?
1132
demandé sur James McNellis 2010-08-30 19:02:41

10 réponses

je suppose que ce document pourrait servir comme une introduction pas si courte : n3055

tout le massacre a commencé avec la sémantique de mouvement. Une fois que nous avons des expressions qui peuvent être déplacées et non copiées, soudainement des règles faciles à comprendre ont exigé la distinction entre les expressions qui peuvent être déplacées, et dans quelle direction.

D'après ce que j'imagine basé sur le projet, la distinction de valeur R/l reste le même, seulement dans le contexte du déplacement les choses deviennent confuses.

sont-ils nécessaires? Probablement pas si nous voulons renoncer aux nouvelles fonctionnalités. Mais pour permettre une meilleure optimisation, nous devrions probablement les adopter.

citant n3055 :

  • Un lvalue (soi-disant, historiquement, parce que lvalues pourrait apparaître sur le partie gauche d'une affectation l'expression désigne une fonction ou un objet. [exemple: si E est un expression de type pointeur, puis *E est une expression de valeur se référant à l'objet ou la fonction auxquels E point. Comme autre exemple, l' résultat de l'appel d'une fonction dont l' le type de retour est une référence lvalue est une lvalue.]
  • Un value (un la valeur" expirant") fait également référence à une objet, généralement près de la fin de son durée de vie (de sorte que ses ressources peut être déplacé, par exemple). Un xvalue est le résultat de certains types de expressions impliquant une valeur référence. [Exemple: résultat de l'appel d'une fonction dont l' type de retour est une référence rvalue est une value.]
  • Un glvalue ("généralisée" lvalue) est un lvalue ou un value .
  • Un rvalue (appelle, historiquement, parce que rvalues pourrait apparaissent sur le côté droit de l' assignment expression) est une valeur de x, un objet temporaire ou sous-objet de celui-ci, ou une valeur qui est pas associée à un objet.
  • A prvalue ("pure" rvalue) est une rvalue ce n'est pas un value. [Exemple: résultat de l'appel d'une fonction dont l' le type de retour n'est pas une référence est un prvalue]

Le document en question est une excellente référence pour cette question, car il montre les changements exacts apportés à la norme à la suite de l'introduction de la nouvelle nomenclature.

542
répondu Kornel Kisielewicz 2010-08-30 15:26:10

Quelles sont ces nouvelles catégories d'expressions?

le FCD (n3092) a une excellente description:

- une valeur (ainsi appelée, historiquement, parce que lvalues pourrait apparaître sur le partie gauche d'une affectation l'expression désigne une fonction ou objet. [ Exemple: si E est un expression de type pointeur, puis *E est une expression de valeur se référant à la objet ou fonction auquel E point. Comme autre exemple, le résultat d'appeler une fonction dont le retour le type est une lvalue de référence est un lvalue. fin de l'exemple ]

- Un xvalue (un la valeur" expirant") fait également référence à une objet, généralement près de la fin de son (de sorte que ses ressources peuvent être déplacé, par exemple). Un value est la résultat de certains types d'expressions impliquant des références de valeur (8.3.2). [ Exemple: Le résultat de l'appel d'une fonction dont le type de retour est un R valeur de référence est une valeur X. -fin exemple]

- Un glvalue ("généralisée" lvalue) est une valeur L ou une xvalue.

- Une valeur (ainsi appelée, historiquement, parce que les valeurs R pourraient apparaître sur le côté droit d'une affectation expressions) est une valeur X, un temporaire l'objet (12.2) ou son sous-objet, ou une valeur qui n'est pas associée à une objet.

- Un prvalue (valeur" pure") est une valeur qui n'est pas une xvalue. [ Exemple: Le résultat de l'appel d'une fonction dont le type de retour n'est pas un la référence est une prvalue. La valeur d'un littéralement comme 12, 7.3e5, ou true is aussi un prvalue. fin de l'exemple ]

chaque l'expression appartient exactement à l'un des les fondamentaux de la classification cette taxonomie: lvalue, value, ou prvalue. Cette propriété d'un l'expression est appelée sa valeur catégorie. [ Note: la discussion sur chaque opérateur intégré dans la Clause 5 indique la catégorie de la valeur qu'il les rendements et les catégories de valeur opérandes il attend. Par exemple, l' les opérateurs d'affectation intégrés s'attendent que l'opérande de gauche est une valeur et que l'opérande juste est une valeur PR et donner une valeur réelle au résultat. Les opérateurs définis par l'utilisateur sont des fonctions, et les catégories de valeurs qu'ils attendre et le rendement sont déterminés par leur type de paramètre et de retour. -fin note

je vous suggère de lire la section entière 3.10 Lvalues et rvalues cependant.

Comment ces nouvelles catégories se rapportent à l'existant rvalue et lvalue catégories?

de nouveau:

Taxonomy

les catégories rvalue et lvalue en C++0x sont-elles les mêmes qu'en C++03?

la sémantique des valeurs R a évolué particulièrement avec l'introduction de la sémantique des mouvements.

pourquoi ces nouvelles catégories sont-elles nécessaires?

de sorte que la construction/l'affectation du déménagement puisse être définie et appuyée.

308
répondu dirkgently 2015-04-21 00:05:16

je commencerai par votre dernière question:

pourquoi ces nouvelles catégories sont-elles nécessaires?

La norme C++ contient de nombreuses règles qui traitent de la valeur d'une expression. Certaines règles font une distinction entre la valeur l et la valeur R. Par exemple, quand il s'agit de résolution de surcharge. D'autres règles font une distinction entre glvalue et prvalue. Par exemple, vous pouvez avoir une glvalue avec un résumé incomplet ou type mais il n'y a pas de prvalue avec un type incomplet ou abstrait. Avant que nous ayons cette terminologie, les règles qui ont réellement besoin de faire la distinction entre glvalue/prvalue se référaient à lvalue / rvalue et elles étaient soit involontairement fausses, soit contenaient beaucoup d'explications et d'exceptions à la règle a la "...sauf si la valeur est due à une référence de valeur sans nom...". C'est donc une bonne idée de donner aux concepts de glvalues et prvalues leur propre nom.

Quelles sont ces nouvelles catégories d'expressions? Comment ces nouvelles catégories se rapportent à l'existant rvalue et lvalue catégories?

nous avons encore les Termes lvalue et rvalue qui sont compatibles avec C++98. Nous avons divisé les valeurs R en deux sous-groupes, les valeurs X et les valeurs PR, et nous appelons les valeurs L et les valeurs X les valeurs GL. Xvalues est un nouveau type de catégorie de valeur pour les références rvalue sans nom. Chaque expression est une de ces trois: lvalue, xvalue, prvalue. Un diagramme de Venn ressemblerait à ceci:

    ______ ______
   /      X      \
  /      / \      \
 |   l  | x |  pr  |
  \      \ /      /
   \______X______/
       gl    r

exemples avec fonctions:

int   prvalue();
int&  lvalue();
int&& xvalue();

mais n'oubliez pas non plus que les références nommées rvalue sont lvalues:

void foo(int&& t) {
  // t is initialized with an rvalue expression
  // but is actually an lvalue expression itself
}
162
répondu sellibitze 2015-09-07 01:03:02

pourquoi ces nouvelles catégories sont-elles nécessaires? Est-ce que les dieux du GT21 essaient juste de nous confondre, simples mortels?

Je ne pense pas que les autres réponses (aussi bonnes soient-elles) saisissent vraiment la réponse à cette question particulière. Oui, ces catégories et autres existent pour permettre la sémantique des mouvements, mais la complexité existe pour une raison. C'est la seule règle inviolée de déplacer des choses en C++11:

Tu ne déplacer que lorsqu'elle est absolument sans danger.

C'est pourquoi ces catégories existent: être capable de parler de valeurs là où il est sécuritaire de s'en écarter, et parler de valeurs là où ce n'est pas le cas.

dans la première version des références de valeur r, le mouvement se produisait facilement. trop facilement. Assez facilement qu'il y avait beaucoup de potentiel pour déplacer implicitement des choses quand l'utilisateur n'a pas vraiment dire.

Voici les circonstances dans lesquelles il est sécuritaire de déplacer quelque chose:

  1. Lorsqu'il s'agit d'un objet temporaire ou secondaire. (prvalue)
  2. Lorsque l'utilisateur a explicitement dit de le déplacer .

si vous faites cela:

SomeType &&Func() { ... }

SomeType &&val = Func();
SomeType otherVal{val};

Qu'est-ce que ça fait? Dans les versions plus anciennes de la spécification, avant que les 5 valeurs soient entrées, cela provoquer un déplacement. Bien sûr, il n'. Vous avez passé une référence rvalue au constructeur, et ainsi il se lie au constructeur qui prend une référence rvalue. C'est une évidence.

il n'y a qu'un problème avec cela; vous n'avez pas demander de le déplacer. Oh, vous pourriez dire que le && aurait dû être un indice, mais cela ne change pas le fait qu'il a enfreint la règle. val n'est pas temporaire car les temporairesn'ont pas de nom. Vous pouvez avoir prolongé la durée de vie du temporaire, mais cela signifie qu'il n'est pas temporaire ; c'est juste comme n'importe quelle autre variable de pile.

si ce n'est pas temporaire, et que vous n'avez pas demandé à le déplacer, alors le est erroné.

la solution évidente est de faire de val une valeur de l. Cela signifie que vous ne pouvez pas vous en écarter. OK, d'accord, c'est nommé, donc c'est une valeur.

une Fois que vous faites cela, vous peut plus dire que SomeType&& signifie la même chose partout. Vous avez maintenant fait une distinction entre les références rvalue nommées et les références rvalue sans nom. Eh bien, les références rvalue sont des références lvalues; c'était notre solution ci-dessus. Alors, qu'est-ce qu'on appelle des références rvalue sans nom (la valeur de retour de Func ci-dessus)?

ce n'est pas une valeur l, Parce que vous ne pouvez pas vous déplacer d'une valeur l. Et nous besoin pour pouvoir se déplacer en retournant un && ; comment pourriez-vous préciser explicitement déplacer quelque chose? C'est ce que std::move renvoie, après tout. Ce n'est pas une valeur (ancienne), parce qu'elle peut être du côté gauche d'une équation (les choses sont en fait un peu plus compliquée, voir cette question et les commentaires ci-dessous). Ce n'est ni une valeur L ni une valeur R; c'est un nouveau genre de chose.

Ce que nous avons est une valeur que vous pouvez traiter comme une lvalue, sauf qu'il est implicitement mobiles. On appelle ça une xvalue.

notez que les valeurs X sont ce qui nous fait gagner les deux autres catégories de valeurs:

  • une prvalue est vraiment juste le nouveau nom pour le type précédent de rvalue, i.e. ils sont les rvalues que ne sont pas xvalues.

  • Glvalues sont l'union de xvalues et lvalues dans un groupe, parce qu'ils ne partagent un beaucoup de propriétés communes.

donc vraiment, tout se résume à xvalues et la nécessité de restreindre le mouvement à exactement et seulement certains endroits. Ces lieux sont définis par la catégorie rvalue; prvalues sont les mouvements implicites, et xvalues sont les mouvements explicites ( std::move renvoie une xvalue).

137
répondu Nicol Bolas 2018-07-29 19:14:36

IMHO, la meilleure explication sur son sens nous a donné Stroustrup + prendre en compte des exemples de Dániel Sándor et Mohan :

Stroustrup:

maintenant j'étais sérieusement inquiet. Clairement nous nous dirigions vers une impasse ou un désordre, ou les deux. J'ai passé le déjeuner à faire une analyse pour voir les propriétés (des valeurs) étaient indépendantes. Il n'y avait que deux propriétés indépendantes:

  • has identity – i.e. et l'adresse, un pointeur, l'utilisateur peut déterminer si deux copies sont identiques,etc.
  • can be moved from - i.e. nous sommes autorisés à laisser à la source d'une "copie" dans un état indéterminé, mais valide

cela m'a conduit à la conclusion qu'il y a exactement trois sortes de valeurs (en utilisant l'astuce de regex notational d'utiliser une lettre majuscule à indiquer un négatif – j'étais pressé):

  • iM : a une identité et ne peut pas être déplacé de
  • im : a une identité et peut être déplacé de (par exemple le résultat de la coulée d'une valeur à une référence de valeur)
  • Im : n'a pas d'identité et peut être déplacé à partir de La quatrième possibilité ( IM : n'a pas d'identité et ne peut pas être déplacé) n'est pas utile dans C++ (ou, je pense) dans toute autre langue.

outre ces trois classifications fondamentales de valeurs, nous deux évident généralisations qui correspondent à deux propriétés indépendantes:

  • i : a une identité
  • m : peut être déplacé de

cela m'a conduit à mettre ceci diagramme sur le tableau: enter image description here

appellation

j'ai observé que nous n'avions qu'une liberté limitée à nommer: les deux points à la gauche (étiquetée iM et i ) sont ce que les gens avec plus ou moins formalité ont appelé lvalues et les deux points sur la droite (étiquetés m et Im ) sont ce que les gens avec plus ou moins de formalité ont appelé rvalues . Cela doit se refléter dans notre appellation. C'est, le " leg "gauche du W devrait avoir des noms liés à lvalue et le le "leg" droit du W devrait avoir des noms liés à rvalue. je note que toute cette discussion/problème de l'introduction de rvalue références et sémantique des mouvements. Ces notions n'existent tout simplement pas dans Strachey's world composé de juste rvalues et lvalues . Quelqu'un observé que les idées que

  • chaque value est soit un lvalue ou un rvalue
  • Un lvalue n'est pas un rvalue et un rvalue n'est pas un lvalue

sont profondément ancrés dans notre conscience, des propriétés très utiles, et des traces de cette dichotomie peuvent être trouvées partout dans le projet de norme. Nous tous d'accord que nous devons préserver ces propriétés (et les faire précis.) Cela a limité davantage nos choix de noms. J'ai observé que le libellé standard de la bibliothèque utilise rvalue pour signifier m (la généralisation), de sorte que, pour préserver l'attente et le texte de la bibliothèque standard le point inférieur droit du W doit être nommé rvalue.

cela a conduit à une discussion ciblée de la dénomination. Tout d'abord, nous avons dû choisir sur lvalue. devrait lvalue signifie iM ou le généralisation i ? LED par Doug Gregor, nous avons énuméré les endroits dans la formulation de langue de base où le mot lvalue a été qualifié pour le dire l'un ou l'autre. Un la liste a été faite, et dans la plupart des cas et dans le plus délicat/fragile texte lvalue signifie actuellement iM . C'est le sens classique de lvalue parce que "dans les vieux jours" rien n'a été bougé; move est une notion nouvelle dans C++0x . En outre, le nom du point topleft de la W lvalue nous donne la propriété que chaque valeur est une lvalue ou un rvalue , mais pas les deux.

ainsi, le point supérieur gauche du W est lvalue et le point inférieur droit est-ce que rvalue. fait les points en bas à gauche et en haut à droite? Le point en bas à gauche est une généralisation de la valeur l Classique, permettant de se déplacer. Donc c'est un generalized lvalue. que nous l'avons appelé glvalue. vous pouvez vous plaindre de l'abréviation, mais (je pense) pas avec la logique. Nous avons supposé que dans les utilisations sérieuses generalized lvalue serait en quelque sorte abrégé de toute façon, donc nous ferions mieux de le faire immédiatement (ou risque de confusion). Le point en haut à droite du W est moins général de en bas à droite maintenant, comme toujours, appelé rvalue ). Que point représente la notion pure originale d'un objet que vous pouvez déplacer parce qu'il ne peut plus être mentionné (sauf par un destructeur). J'ai aimé la phrase specialized rvalue en contraste avec generalized lvalue mais pure rvalue Abrégé en prvalue gagné (et probablement à juste titre). Donc, la jambe gauche du W est lvalue et glvalue et la jambe droite est prvalue et rvalue. soit dit en passant, chaque valeur est soit une glvalue, soit une prvalue, mais pas les deux.

cela laisse le milieu supérieur du W : im ; c'est-à-dire, les valeurs qui ont identité et peut être déplacé. Nous n'avons vraiment pas quelque chose que les guides nous un bon nom pour ces bêtes ésotériques. Ils sont importants pour les personnes qui travaillent avec le projet de texte standard, mais sont peu susceptibles de devenir un nom de ménage. Nous n'avons trouvé aucune contrainte réelle sur le naming pour nous guider, nous avons donc choisi " x " pour le centre, l'inconnu, l' étrange, le xpert seulement, ou même x-rated.

Steve showing off the final product

100
répondu Ivan Kush 2017-05-23 12:26:33

INTRODUCTION

ISOC++11 (officiellement ISO/IEC 14882:2011) est la version la plus récente de la norme du langage de programmation C++. Il contient quelques nouvelles fonctionnalités et des concepts, par exemple:

  • références rvalue
  • value, glvalue, prvalue valeur de l'expression des catégories
  • sémantique de déplacement

si nous voulons pour comprendre les concepts des nouvelles catégories de valeurs d'expression, nous devons être conscients qu'il existe des références à la valeur de R et à la valeur de l. Il est préférable de savoir que les valeurs R peuvent être transmises à des références à des valeurs non conformes.

int& r_i=7; // compile error
int&& rr_i=7; // OK

nous pouvons avoir une certaine intuition des concepts de catégories de valeurs si nous citons la sous-section intitulée Lvalues et rvalues du document de travail N3337 (le projet le plus similaire à la norme ISOC++11 publiée).

3,10 Lvalues et valeurs [basic.lval]

1 Les Expressions sont classées selon la taxinomie de la Figure 1.

  • une valeur l (appelée ainsi, historiquement, parce que les valeurs l pourraient apparaître sur le côté gauche d'une expression assignée) désigne une fonction ou un objet. [ Exemple: si E est une expression de type pointeur, alors *E est une expression de valeur l se référant à l'objet ou fonction vers laquelle E Pointe. Comme autre exemple, le résultat de l'appel d'une fonction dont le type de retour est une référence lvalue est une lvalue. fin de l'exemple ]
  • une valeur X (une valeur" expirante") se réfère également à un objet, généralement vers la fin de sa vie (de sorte que ses ressources peuvent être déplacées, exemple.) Un value est le résultat de certains types d'expressions impliquant des références de valeur (8.3.2). [ Exemple: Le résultat de l'appel une fonction dont le type de retour est un R valeur de référence est une valeur X. -fin exemple]
  • Un glvalue ("généralisée" lvalue) est une lvalue ou une value.
  • une valeur (ainsi appelée, historiquement, parce que des valeurs R pourraient apparaître sur le côté droit d'une expression d'affectation) est une valeur X, a

    objet temporaire (12.2) ou son sous-objet, ou une valeur qui n'est pas

    associé à un objet.
  • A prvalue ("pure" rvalue) est une valeur qui n'est pas une xvalue. [ Exemple: Le résultat de l'appel d'une fonction dont le type de retour n'est pas un

    la référence est une prvalue. La valeur d'un littéral comme 12, 7.3e5, ou

    vrai, c'est aussi un prvalue. fin de l'exemple ]

toute expression appartient exactement à l'un des fondamentaux classifications dans cette taxonomie: lvalue, xvalue, ou prvalue. Ce propriété d'un l'expression est appelée sa catégorie de valeur.

mais je ne suis pas tout à fait sûr que cette sous-section est suffisante pour comprendre les concepts clairement, parce que "habituellement" n'est pas vraiment général, "près de la fin de sa vie" n'est pas vraiment concret, "impliquant des références de valeur" n'est pas vraiment clair, et "exemple: le résultat de l'appel d'une fonction dont le type de retour est une référence de valeur est une xvalue."sonne comme un serpent qui se mord la queue.

CATÉGORIES DE VALEUR PRIMAIRE

chaque expression appartient exactement à une catégorie de valeur primaire. Ces catégories de valeur sont les catégories lvalue, xvalue et prvalue.

lvalues

l'expression E appartient à la catégorie lvalue si et seulement si E se réfère à une entité qui a déjà eu une identité (adresse, nom ou alias) qui la rend accessible en dehors de E.

#include <iostream>

int i=7;

const int& f(){
    return i;
}

int main()
{
    std::cout<<&"www"<<std::endl; // This address ...
    std::cout<<&"www"<<std::endl; // ... and this address are the same.
    "www"; // The expression "www" in this row is an lvalue expression, because it refers to the same entity ...
    "www"; // ... as the entity the expression "www" in this row refers to.

    i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression i in this row refers to.

    int* p_i=new int(7);
    *p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
    *p_i; // ... as the entity the expression *p_i in this row refers to.

    const int& r_I=7;
    r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
    r_I; // ... as the entity the expression r_I in this row refers to.

    f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression f() in this row refers to.

    return 0;
}

xvalues

l'expression E appartient à la catégorie xvalue si et seulement si elle est

- le résultat de l'appel d'une fonction, implicitement ou explicitement, dont le type de retour est une référence de valeur au type d'objet retourné, ou "1519690920

int&& f(){
    return 3;
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.

    return 0;
}

- un moulage à une valeur de référence du type d'objet, ou

int main()
{
    static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
    std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).

    return 0;
}

- expression d'accès d'un membre de classe désignant un membre de données non statiques de type non de référence dans lequel l'expression d'objet est une valeur X, ou "1519690920

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.

    return 0;
}

- une expression pointeur-à-Membre dans laquelle le premier opérande est une valeur X et le second opérande est un pointeur vers un membre de données.

noter que l'effet des règles ci-dessus est que les références à des objets nommés rvalue sont traitées comme des références à des objets nommés lvalues et les références à des objets sans nom rvalue sont traitées comme des xvalues; les références à des fonctions nommées rvalue sont traitées comme des lvalues, qu'elles soient nommées ou non.

#include <functional>

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
    As&& rr_a=As();
    rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
    std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.

    return 0;
}

prvalues

l'expression E appartient à la catégorie prvalue si et seulement si E n'appartient ni à la lvalue ni à la xvalue catégorie.

struct As
{
    void f(){
        this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
    }
};

As f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.

    return 0;
}

CATÉGORIES DE VALEUR MIXTE

il existe deux autres catégories importantes de valeurs mixtes. Ces catégories de valeur sont des catégories de valeur R et glvalue.

rvalues

l'expression E appartient à la catégorie de la valeur R Si et seulement si E appartient à la catégorie de la valeur X ou à la catégorie de la valeur PR.

il est à noter que cette définition signifie que L'expression E appartient à la catégorie de la valeur de référence si et seulement si E se réfère à une entité qui n'a pas encore d'identité qui la rend accessible en dehors de E.

glvalues

l'expression E appartient à la catégorie de la valeur GLV si et seulement si E appartient à la catégorie de la valeur L ou à la catégorie de la valeur X.

A PRACTICAL RULE

Scott Meyer a publié un très utile règle de pouce pour distinguer les rvalues de lvalues.

  • Si vous pouvez prendre l'adresse d'une expression, l'expression est une lvalue.
  • si le type d'une expression est une référence de valeur l (Par exemple, T& ou const T&, etc.), cette expression est une lvalue.
  • sinon, l'expression est une valeur. Sur le plan conceptuel (et généralement aussi en fait), les valeurs R correspondent à des objets temporaires, tels que comme ceux retournés des fonctions ou créés par type implicite conversion. La plupart des valeurs littérales (p. ex. 10 et 5.3) sont également des valeurs R.
34
répondu Dániel Sándor 2016-03-07 11:26:10

les catégories de C++03 sont trop restreintes pour saisir correctement l'introduction des références de valeur dans les attributs d'expression.

avec leur introduction, il a été dit qu'une référence rvalue sans nom évalue une valeur R, de sorte que la résolution de surcharge préférerait les fixations de référence rvalue, ce qui lui ferait sélectionner les constructeurs move plutôt que les constructeurs de copie. Mais il a été constaté que cela provoque des problèmes tout autour, par exemple avec Dynamique Types et avec qualifications.

pour montrer ceci, considérez

int const&& f();

int main() {
  int &&i = f(); // disgusting!
}

Sur la pré-value brouillons, cela était autorisé, car en C++03, rvalues de non-types de classe ne sont jamais de cv qualifiés. Mais il est prévu que const s'applique dans le cas rvalue-référence, car ici nous do se référer à des objets (=mémoire!), et la chute de const des valeurs hors classe R est principalement pour la raison qu'il n'y a pas d'objet autour.

la question des types dynamiques est de nature similaire. En C++03, Les valeurs de type de classe ont un type dynamique connu - c'est le type statique de cette expression. Parce que pour l'avoir d'une autre façon, vous avez besoin de références ou déréférences, qui évaluent à une valeur de l. Ce n'est pas vrai avec des références rvalue sans nom, mais elles peuvent montrer un comportement polymorphe. Alors pour le résoudre,

  • sans nom références rvalue devenir xvalues . Ils peuvent être qualifiés et avoir leur type dynamique différent. Comme prévu, ils préfèrent les références rvalue lors de la surcharge et ne se lient pas aux références non conformes.

  • ce qui était auparavant une valeur (littérales, objets créés par des moulages à des types non-de référence) devient maintenant une prvalue . Ils ont la même préférence que xvalues lors de la surcharge.

  • ce qui était auparavant une valeur reste une valeur.

et deux regroupements sont effectués pour saisir ceux qui peuvent être qualifiés et peuvent avoir différents types dynamiques ( glvalues ) et ceux où la surcharge préfère la liaison rvalue de référence ( rvalues ).

32
répondu Johannes Schaub - litb 2010-08-30 17:04:01

j'ai lutté avec cela pendant longtemps, jusqu'à ce que je suis tombé sur le cppreference.com explication des catégories de valeur 151920920".

Il est en réalité assez simple, mais je trouve que c'est souvent expliqué d'une manière qui est difficile à mémoriser. Ici est expliqué très schématiquement. Je vais citer quelques parties de la page:

catégories primaires

les catégories de valeur primaire correspondent à deux propriétés des expressions:

  • a l'identité : il est possible de déterminer si l'expression se réfère à la même entité qu'une autre expression, par exemple en comparant les adresses des objets ou les fonctions qu'ils identifient (obtenus directement ou indirectement);

  • peut être déplacé de : constructeur de déplacement, affectation de déplacement opérateur, ou une autre surcharge de fonction qui met en œuvre la sémantique des mouvements peut se lier à l'expression.

Expressions qui:

  • identité et ne peut pas être déplacé de sont appelés lvalue expressions ;
  • ont une identité et peuvent être déplacés sont appelés xvalue expressions ;
  • n'ont pas d'identité et peuvent être déplacés de sont appelés prvalue expressions ;
  • n'ont pas d'identité et ne peut pas être déplacé de ne sont pas utilisés.

lvalue

une expression lvalue ("valeur de gauche") est une expression qui a une identité et ne peut pas être déplacée de .

valeur (jusqu'à C++11), prvalue (depuis C++11)

une expression prvalue ("pure valeur") est une expression qui n'a pas d'identité et peut être déplacée de .

value

une expression xvalue ("expiring value") est une expression qui a une identité et peut être déplacée de .

glvalue

Un glvalue ("généralisée lvalue") l'expression est une expression qui est une lvalue ou une value. a identité . Il peut ou ne peut pas être déplacé.

valeur (depuis C++11)

une expression rvalue ("right value") est une expression qui est soit prvalue, soit xvalue. peut être déplacé de . Il peut ou peut ne pas avoir d'identité.

23
répondu Felix Dombek 2016-06-17 02:20:32

Comment ces nouvelles catégories se rapportent à l'existant rvalue et lvalue catégories?

A C++03 lvalue est toujours une valeur C++11, alors qu'une valeur C++03 R est appelée prvalue en C++11.

15
répondu fredoverflow 2017-07-31 12:55:30

un addendum aux excellentes réponses ci-dessus, sur un point qui m'a embrouillé même après que j'ai lu Stroustrup et pensé que j'ai compris la distinction rvalue/lvalue. Quand vous voyez

int&& a = 3 ,

il est très tentant de lire le int&& comme un type et de conclure que a est une valeur. Ce n'est pas:

int&& a = 3;
int&& c = a; //error: cannot bind 'int' lvalue to 'int&&'
int& b = a; //compiles

a a un nom et est ipso facto une valeur. ne pensez pas au && comme faisant partie du type de a ; c'est juste quelque chose qui vous dit ce à quoi a est autorisé à se lier.

cela concerne particulièrement les arguments de type T&& dans les constructeurs. Si vous écrivez

Foo::Foo(T&& _t) : t{_t} {}

vous copierez _t dans t . Vous avez besoin de

Foo::Foo(T&& _t) : t{std::move(_t)} {} si vous voulez déplacer. Serait-ce mon compilateur il m'a prévenu quand j'ai oublié le move !

13
répondu Mohan 2016-07-25 04:26:13