Question Quels sont les valeurs, lvalues, xvalues, glvalues ​​et prvalues?


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

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

  1. Rvalue
  2. lvalue
  3. xvalue
  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-elles aux catégories actuelles de valeur et de lvalue?
  • 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 les WG21 dieux essayant juste de nous confondre de simples mortels?

1104
2017-08-30 15:02


origine


Réponses:


Je suppose que ce document pourrait servir d'introduction pas si courte: n3055

Tout le massacre a commencé avec le mouvement sémantique. Une fois que nous avons des expressions qui peuvent être déplacées et non copiées, des règles faciles à appréhender requièrent une distinction entre les expressions qui peuvent être déplacées et dans quelle direction.

D'après ce que je suppose basé sur le brouillon, la distinction de la valeur r / l reste la même, seulement dans le contexte des choses en mouvement sont en désordre.

Sont-ils nécessaires? Probablement pas si nous souhaitons 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 côté gauche d'une affectation expression) désigne une fonction ou un objet. [Exemple: Si E est un expression du type de pointeur, puis *E est une expression lvalue se référant à l'objet ou la fonction à laquelle E points. Comme autre exemple, le résultat de l'appel d'une fonction dont type de retour est une référence lvalue est une lvalue.] 
  • Un xvalue (un "Valeur eXpiring") se réfère également à objet, généralement près de la fin de son vie (afin que ses ressources puissent être déplacé, par exemple). Une valeur x est le résultat de certains types de expressions impliquant rvalue les références. [Exemple: le résultat de l'appel d'une fonction dont type de retour est une référence rvalue est une valeur x.]
  • UNE Glvalue   (Lvalue "généralisée") est un lvalue ou un xvalue.
  • Un Rvalue (soi-disant, historiquement, parce que les valeurs pouvaient apparaît sur le côté droit d'un expression d'affectation) est une valeur x, un objet temporaire ou sous-objet de celui-ci, ou une valeur qui est pas associé à un objet.
  • UNE prvalue ("Pure" rvalue) est une rvalue ce n'est pas une valeur x. [Exemple: le résultat de l'appel d'une fonction dont 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 dans la norme qui ont eu lieu à la suite de l'introduction de la nouvelle nomenclature.


530
2017-08-30 15:09



Quelles sont ces nouvelles catégories d'expressions?

le FCD (n3092) a une excellente description:

- Une lvalue (ainsi appelée, historiquement, parce que les lvalues ​​pouvaient apparaître sur le   côté gauche d'une affectation   expression) désigne une fonction ou   un objet. [Exemple: si E est un   expression du type de pointeur, puis   * E est une expression lvalue se référant à l'objet ou à la fonction à laquelle E   points. Comme autre exemple, le résultat   d'appeler une fonction dont le retour   type est une référence lvalue est un   lvalue. -end exemple]

- Une valeur x (un   "Valeur eXpiring") se réfère également à   objet, généralement près de la fin de son   vie (afin que ses ressources puissent être   déplacé, par exemple). Une valeur x est la   résultat de certains types d'expressions   impliquant des références de référence (8.3.2). [   Exemple: Le résultat de l'appel d'un   fonction dont le type de retour est un   La référence rvalue est une valeur x. -fin   Exemple ]

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

-   Un rvalue (soi-disant, historiquement,   parce que les valeurs peuvent apparaître sur le   côté droit d'une affectation   expressions) est un xvalue, un temporaire   objet (12.2) ou sous-objet de celui-ci, ou   une valeur qui n'est pas associée à un   objet.

- Une prvalue (valeur "pure") est   une valeur qui n'est pas une valeur x. [   Exemple: Le résultat de l'appel d'un   fonction dont le type de retour n'est pas un   la référence est une prvalue. La valeur d'un   littéral tel que 12, 7.3e5, ou vrai est   aussi une prvalue. -end exemple]

Chaque   l'expression appartient à exactement l'un des   les classifications fondamentales dans   cette taxonomie: lvalue, xvalue, ou   prvalue. Cette propriété d'un   l'expression est appelée sa valeur   Catégorie. [Note: La discussion de   chaque opérateur intégré à l'Article 5   indique la catégorie de la valeur   rendements et les catégories de valeur de la   opérandes qu'il attend. Par exemple, le   opérateurs d'assignation intégrés attendent   que l'opérande gauche est une lvalue et   que l'opérande de droite est une prvalue   et donne une lvalue comme 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   leurs paramètres et types de retour. -fin   Remarque

Je vous suggère de lire toute la section 3.10 Lvalues ​​et valeurs bien que.

Comment ces nouvelles catégories se rapportent-elles aux catégories actuelles de valeur et de lvalue?

Encore:

Taxonomy

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

La sémantique des valeurs a évolué en particulier avec l'introduction de la sémantique des déplacements.

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

Donc, la construction / l'affectation du déménagement pourrait être définie et soutenue.


303
2017-08-31 08:08



Je vais commencer 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 catégorie de valeur d'une expression. Certaines règles font une distinction entre lvalue et rvalue. Par exemple, quand il s'agit de la résolution de surcharge. D'autres règles font une distinction entre glvalue et prvalue. Par exemple, vous pouvez avoir un glvalue avec un type incomplet ou abstrait mais il n'y a pas de valeur prvalue avec un type incomplet ou abstrait. Avant que nous ayons cette terminologie, les règles qui ont réellement besoin de distinguer glvalue / prvalue se réfèrent à lvalue / rvalue et elles étaient involontairement fausses ou contenaient beaucoup d'explications et d'exceptions à la règle à la "... sauf si la valeur est due à référence rvalue ... ". Donc, il semble que ce soit une bonne idée de simplement donner aux concepts de glvalues ​​et de prvalues ​​leur propre nom.

Quelles sont ces nouvelles catégories d'expressions?   Comment ces nouvelles catégories se rapportent-elles aux catégories actuelles de valeur et de lvalue?

Nous avons toujours les termes lvalue et rvalue compatibles avec C ++ 98. Nous venons de diviser les valeurs en deux sous-groupes, xvalues ​​et prvalues, et nous nous référons aux valeurs de lvalues ​​et xvalues ​​comme des glvalues. Les valeurs X sont un nouveau type de catégorie de valeur pour les références rvalue non nommées. Chaque expression est l'une de ces trois: lvalue, xvalue, prvalue. Un diagramme de Venn ressemblerait à ceci:

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

Exemples avec des fonctions:

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

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

void foo(int&& t) {
  // t is initialized with an rvalue expression
  // but is actually an lvalue expression itself
}

156
2018-03-04 06:32



Pourquoi ces nouvelles catégories sont-elles nécessaires? Les dieux du WG21 essayent-ils simplement de nous confondre avec de simples mortels?

Je ne pense pas que les autres réponses (bien que bon nombre d'entre eux) capturent vraiment la réponse à cette question particulière. Oui, ces catégories existent pour permettre la sémantique du mouvement, mais la complexité existe pour une raison. C'est la règle inviolable de déplacer des choses dans C ++ 11:

Tu ne bougeras que quand il est indubitablement sûr de le faire.

C'est pourquoi ces catégories existent: être capable de parler de valeurs où il est sûr de s'en écarter et de parler de valeurs là où elles ne le sont pas.

Dans la première version des références de valeur r, le mouvement s'est passé facilement. Aussi facilement. Assez facilement qu'il y avait beaucoup de potentiel pour déplacer des choses implicitement lorsque l'utilisateur ne voulait pas vraiment.

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

  1. Quand c'est un temporaire ou un sous-objet de celui-ci. (prvalue)
  2. Lorsque l'utilisateur a explicitement dit de le déplacer.

Si tu fais ça:

SomeType &&Func() { ... }

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

Qu'est-ce que ça fait? Dans les anciennes versions de la spécification, avant que les 5 valeurs ne soient entrées, cela provoquait un mouvement. Bien sûr que oui. Vous avez passé une référence rvalue au constructeur, et donc il se lie au constructeur qui prend une référence rvalue. Cela est évident.

Il y a juste un problème avec ça; tu n'as pas demander pour 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 parce que les temporaires n'ont pas de noms. Vous avez peut-être prolongé la durée de vie du temporaire, mais cela signifie qu'il n'est pas temporaire; c'est comme n'importe quelle autre variable de pile.

Si ce n'est pas temporaire, et vous n'avez pas demandé de le déplacer, alors le déplacement est faux.

La solution évidente est de faire val une lvalue. Cela signifie que vous ne pouvez pas en sortir. OK bien; c'est nommé, donc c'est une lvalue.

Une fois que vous faites cela, vous ne pouvez 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 non nommées. Eh bien, les références rvalue nommées sont des valeurs; c'était notre solution ci-dessus. Alors qu'est-ce que nous appelons des références rvalue sans nom (la valeur de retour de Func au dessus)?

Ce n'est pas une lvalue, parce que vous ne pouvez pas passer d'une lvalue. Et nous avoir besoin être en mesure de se déplacer en retournant un &&; comment pourriez-vous dire explicitement pour déplacer quelque chose? C'est ça std::moveretourne, après tout. Ce n'est pas un rvalue (à l'ancienne), car il peut être sur le côté gauche d'une équation (les choses sont en fait un peu plus compliquées, voir cette question et les commentaires ci-dessous). Ce n'est ni une lvalue ni une valeur; 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 mobile de. Nous l'appelons une valeur x.

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

  • Un prvalue est vraiment juste le nouveau nom pour le type précédent de rvalue, c'est-à-dire qu'ils sont les valeurs qui ne sont pas xvalues.

  • Les Glvalues ​​sont l'union de xvalues ​​et de lvalues ​​dans un groupe, car elles partagent beaucoup de propriétés communes.

Donc vraiment, tout se résume à des valeurs 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 valeur x).


130
2017-07-03 12:30



À mon humble avis, la meilleure explication sur sa signification 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 gâchis ou les deux. J'ai passé l'heure du déjeuner à faire une analyse pour voir   des propriétés (des valeurs) étaient indépendantes. Il n'y avait que deux   propriétés indépendantes:

  • has identity - c'est-à-dire et adresse, un pointeur, l'utilisateur peut déterminer si deux copies sont identiques, etc.
  • can be moved from - c'est-à-dire que 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 existe exactement trois types de   valeurs (en utilisant l'astuce de notation regex d'utiliser une lettre majuscule à   indiquer un négatif - j'étais pressé):

  • iM: a une identité et ne peut être déplacé de
  • im: a une identité et peut être déplacé de (par exemple, le résultat de la conversion d'une valeur lvalue en une valeur de référence)
  • Im: n'a pas d'identité et peut être déplacé 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 n'importe quelle autre langue.

En plus de ces trois classifications fondamentales des valeurs, nous   avoir deux généralisations évidentes qui correspondent aux deux   propriétés indépendantes:

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

Cela m'a conduit à mettre ce diagramme sur le tableau:    enter image description here

Appellation

J'ai observé que nous n'avions qu'une liberté limitée de 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   (marqué m et Im) sont ce que les gens avec plus ou moins formalité   a appelé rvalues. Cela doit être reflété dans notre nom. C'est,   la «jambe» gauche du W devrait avoir des noms liés à lvalue et le   "jambe" droite du W devrait avoir des noms liés à rvalue. je note   que toute cette discussion / problème provient de l'introduction de   rvaluez les références et déplacez la sémantique. Ces notions n'existent tout simplement pas   dans le monde de Strachey consistant juste rvalues et lvalues. Quelqu'un   observé que les idées

  • 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 ont convenu que nous devons préserver ces propriétés (et les rendre   précis). Cela a encore limité nos choix de noms. J'ai observé que   le libellé de la bibliothèque standard utilise rvalue vouloir dire m (la   généralisation), afin de préserver l'attente et le texte de   bibliothèque standard le point bas droit de la W devrait être nommé    rvalue.

Cela a conduit à une discussion ciblée sur la dénomination. Premièrement, nous devions décider   sur lvalue. Devrait lvalue signifier iM ou la généralisation i? LED   par Doug Gregor, nous avons énuméré les endroits dans la formulation du langage de base   où le mot lvalue était qualifié pour signifier l'un ou l'autre. UNE   liste a été faite et dans la plupart des cas et dans le texte le plus délicat / cassant    lvalue signifie actuellement iM. C'est la signification classique de la lvalue   parce que "dans l'ancien temps" rien n'était déplacé; move est une notion nouvelle   dans C++0x. En outre, nommer le point topleft du W  lvalue nous donne   la propriété que chaque valeur est un lvalue ou un rvalue, mais pas les deux.

Donc, le point en haut à gauche de W est lvalue et le point en bas à droite   est rvalue. Qu'est-ce que cela fait en bas à gauche et en haut à droite?   Le point en bas à gauche est une généralisation de la lvalue classique,   permettant le déplacement. Donc c'est un generalized lvalue. Nous l'avons nommé    glvalue. Vous pouvez chipoter sur l'abréviation, mais (je pense) pas   avec la logique. Nous avons supposé que dans un usage sérieux generalized lvalue   serait en quelque sorte abrégé de toute façon, nous ferions mieux de le faire   immédiatement (ou risque de confusion). Le point en haut à droite du W est moins   général que le bas à droite (maintenant, comme toujours, appelé rvalue). Cette   point représente la notion pure d'origine d'un objet que vous pouvez déplacer   de parce qu'il ne peut pas être référé à nouveau (sauf par un destructeur).   J'ai aimé la phrase specialized rvalue contrairement à generalized lvalue mais pure rvalue abréviation de prvalue gagné (et   probablement à juste titre). Ainsi, la jambe gauche du W est lvalue et    glvalue et la jambe droite est prvalue et rvalue. Incidemment,   chaque valeur est soit un glvalue ou une prvalue, mais pas les deux.

Cela laisse le haut du milieu de la W: im; c'est-à-dire, les valeurs qui ont   identité et peut être déplacé. Nous n'avons vraiment rien qui guide   nous à un bon nom pour ces bêtes ésotériques. Ils sont importants pour   personnes travaillant avec le texte standard (projet), mais ne   devenir un nom de ménage. Nous n'avons trouvé aucune contrainte réelle sur le   nommer pour nous guider, nous avons donc choisi «x» pour le centre, l'inconnu, le   étrange, l'xpert seulement, ou même x-rated.

Steve showing off the final product


92
2018-01-20 13:46



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 de nouvelles fonctionnalités et concepts, par exemple:

  • références rvalue
  • Valeurs xvalue, glvalue, prvalue de valeur d'expression
  • déplacer la sémantique

Si nous voulons comprendre les concepts des nouvelles catégories de valeurs d'expression, nous devons être conscients qu'il existe des références de valeurs et de valeurs. Il est préférable de savoir que les valeurs peuvent être passées à des références de type non-const.

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

Nous pouvons acquérir une certaine intuition des concepts de catégories de valeurs si nous citons la sous-section intitulée Lvalues ​​and rvalues ​​du projet 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 taxonomie de la figure 1.

  • Une lvalue (ainsi appelée, historiquement, parce que les valeurs peuvent apparaître sur le côté gauche d'une expression d'affectation) désigne une fonction   ou un objet. [Exemple: Si E est une expression de type pointeur, alors   * E est une expression lvalue se référant à l'objet ou à la fonction à 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. -end exemple]
  • Une valeur x (valeur "eXpiring") fait également référence à un objet, généralement vers la fin de sa durée de vie (afin que ses ressources puissent être déplacées, par exemple).   Exemple). Une valeur x est le résultat de certains types d'expressions   impliquant des références de référence (8.3.2). [Exemple: Le résultat de l'appel   une fonction dont le type de retour est une référence rvalue est une valeur x. -fin   Exemple ]
  • Une glvalue (lvalue "généralisée") est une lvalue ou une valeur x.
  • Un rvalue (ainsi appelé, historiquement, parce que les valeurs peuvent apparaître sur le côté droit d'une expression d'affectation) est une valeur x, un
      objet temporaire (12.2) ou sous-objet de celui-ci, ou une valeur qui n'est pas
      associé à un objet.
  • Une prvalue (rvalue "pure") est une valeur qui n'est pas une valeur x. [Exemple: Le résultat de l'appel d'une fonction dont le type de retour n'est pas
      la référence est une prvalue. La valeur d'un littéral tel que 12, 7.3e5 ou
      vrai est aussi une prvalue. -end exemple]

Chaque expression appartient à exactement l'un des fondamentaux   classifications dans cette taxonomie: lvalue, xvalue, ou prvalue. Ce   La propriété d'une 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 rvalue" et "Exemple: Le résultat de l'appel d'une fonction dont le type de retour est une référence rvalue est une valeur x." On dirait qu'un serpent mord sa queue.

CATÉGORIES DE VALEUR PRIMAIRE

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

lvalues

L'expression E appartient à la catégorie lvalue si et seulement si E fait référence à 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 rvalue au type d'objet renvoyé, ou

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;
}

- une conversion en une référence rvalue au 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;
}

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

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 vers 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.

Notez que l'effet des règles ci-dessus est que les références rvalue nommées aux objets sont traitées comme des valeurs lvalues ​​et que les références rvalues ​​non nommées aux objets sont traitées comme des valeurs x; Les références à des fonctions sont traitées comme des valeurs 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 catégorie lvalue ni à la catégorie xvalue.

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 VALEURS MIXTES

Il existe deux autres catégories de valeurs mixtes importantes. Ces catégories de valeurs sont des catégories rvalue et glvalue.

valeurs

L'expression E appartient à la catégorie rvalue si et seulement si E appartient à la catégorie xvalue, ou à la catégorie prvalue.

Notez que cette définition signifie que l'expression E appartient à la catégorie rvalue si et seulement si E fait référence à une entité qui n'a pas d'identité qui la rend accessible en dehors de E YET.

Glvalues

L'expression E appartient à la catégorie glvalue si et seulement si E appartient à la catégorie lvalue, ou à la catégorie xvalue.

UNE RÈGLE PRATIQUE

Scott Meyer a publié une règle empirique très utile pour distinguer les valeurs des valeurs.

  • 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 lvalue (par exemple, T & ou const T &, etc.), cette expression est une lvalue.
  • Sinon, l'expression est un rvalue. Conceptuellement (et typiquement aussi en fait), les valeurs correspondent à des objets temporaires, tels   comme ceux renvoyés à partir de fonctions ou créés par le biais du type implicite   conversions La plupart des valeurs littérales (par exemple 10 et 5,3) sont également des valeurs.

34
2017-08-30 16:46



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

Avec leur introduction, il a été dit qu'une référence rvalue sans nom évalue à un rvalue, de sorte que la résolution de surcharge préférerait des liaisons de référence rvalue, ce qui le ferait sélectionner des constructeurs de déplacement sur des constructeurs de copie. Mais il a été constaté que cela provoque des problèmes tout autour, par exemple avec Types dynamiques et avec des qualifications.

Pour le montrer, considérez

int const&& f();

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

Sur les ébauches pré-xvalues, cela a été autorisé, car en C ++ 03, les valeurs de type non-classe ne sont jamais qualifiées pour cv. Mais il est prévu que const s'applique dans le cas de rvalue-référence, car ici nous faire se référer à des objets (= mémoire!), et abandonner const de valeurs non-classe est principalement pour la raison qu'il n'y a pas d'objet autour.

Le problème pour les types dynamiques est de nature similaire. En C ++ 03, les valeurs de type classe ont un type dynamique connu - c'est le type statique de cette expression. Parce que pour l'avoir autrement, vous avez besoin de références ou de déréférences, qui évaluent à une lvalue. Ce n'est pas le cas avec les références rvalue non nommées, mais elles peuvent montrer un comportement polymorphe. Donc, pour le résoudre,

  • références rvalue sans nom deviennent xvalues. Ils peuvent être qualifiés et potentiellement avoir leur type dynamique différent. Ils font, comme prévu, préfèrent des références de rvalue pendant la surcharge, et ne lieront pas aux références de lvalue non-const.

  • Ce qui était auparavant un rvalue (littéraux, objets créés par des castings en types non référencés) devient maintenant un prvalue. Ils ont la même préférence que les valeurs x pendant la surcharge.

  • Ce qui était auparavant une lvalue reste une lvalue.

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


32
2018-06-17 02:20



J'ai lutté avec cela pendant longtemps, jusqu'à ce que je suis tombé sur l'explication cppreference.com de la catégories de valeur.

C'est en fait plutôt simple, mais je trouve que c'est souvent expliqué d'une manière qui est difficile à mémoriser. Ici, il est expliqué très schématiquement. Je vais citer quelques parties de la page:

Catégories primaires

Les catégories de valeurs primaires correspondent à deux propriétés d'expressions:

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

  • peut être déplacé de: déplacez le constructeur, déplacez l'opérateur d'affectation ou une autre surcharge de fonction qui implémente la sémantique de déplacement peut se lier à l'expression.

Expressions qui:

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

lvalue

Une expression lvalue ("left value") est une expression qui a une identité et ne peut pas être déplacé de.

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

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

xvalue

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

Glvalue

Une expression glvalue ("lvalue généralisée") est une expression qui est soit une lvalue soit une valeur x. Il a une identité. Il peut ou non être déplacé.

rvalue (depuis C ++ 11)

Une expression rvalue ("right value") est une expression qui est soit une prvalue, soit une valeur x. Il peut être déplacé de. Il peut ou non avoir une identité.


20
2017-08-30 15:45



Comment ces nouvelles catégories se rapportent-elles aux catégories actuelles de valeur et de lvalue?

Un lvalue C ++ 03 est toujours un lvalue C ++ 11, alors qu'un rvalue C ++ 03 est appelé prvalue dans C ++ 11.


15
2017-07-25 04:26