Catégories de valeurs et références à celles-ciValue categories, and references to them

Cette rubrique décrit les différentes catégories de valeurs (et les références aux valeurs) qui existent en C++.This topic describes the various categories of values (and references to values) that exist in C++. Vous aurez sans doute entendu parler de lvalues et de rvalues, mais vous ne les considérez peut-être pas dans les mêmes termes que ceux présentés par cette rubrique.You will doubtless have heard of lvalues and rvalues, but you may not think of them in the terms that this topic presents. Et il existe également d’autres types de valeurs.And there are other kinds of values, too.

Chaque expression en C++ génère une valeur qui appartient à l’une des catégories présentées dans cette rubrique.Every expression in C++ yields a value that belongs to one of the categories discussed in this topic. Certains aspects du langage C++, ses installations et ses règles exigent une bonne compréhension de ces catégories de valeurs et les références à celles-ci.There are aspects of the C++ language, its facilies, and rules, that demand a proper understanding of these value categories, and references to them. Par exemple, prendre l’adresse d’une valeur, copier une valeur, déplacer une valeur et transférer une valeur à une autre fonction.For example, taking the address of a value, copying a value, moving a value, and forwarding a value on to another function. Cette rubrique n’aborde pas tous ces aspects en détail, mais elle fournit des informations de base permettant de bien les comprendre.This topic doesn't go into all of those aspects in depth, but it provides foundational information for a solid understanding of them.

Les informations fournies dans cette rubrique sont encadrées en termes d’analyse de catégories de valeur de Stroustrup par les deux propriétés indépendantes d’identité et de mobilité [Stroustrup, 2013].The info in this topic is framed in terms of Stroustrup's analysis of value categories by the two independent properties of identity and movability [Stroustrup, 2013].

Une lvalue a une identitéAn lvalue has identity

Que signifie, pour une valeur, d’avoir une identité ?What does it mean for a value to have identity? Si vous avez (ou que vous pouvez prendre) l’adresse mémoire d’une valeur et l’utiliser de manière sécurisée, la valeur a une identité.If you have (or you can take) the memory address of a value and use it safely, then the value has identity. Ainsi, vous pouvez effectuer bien plus que comparer le contenu de valeurs : vous pouvez les comparer ou les différencier par identité.That way, you can do more than compare the contents of values: you can compare or distinguish them by identity.

Une lvalue a une identité.An lvalue has identity. Le fait que le « l » dans « lvalue » soit l’abréviation de « left » (gauche) (comme dans le côté gauche d’une assignation) ne présente désormais qu’un intérêt purement historique.It's now a matter of only historical interest that the "l" in "lvalue" is an abbreviation of "left" (as in, the left-hand-side of an assignment). En C++, une lvalue peut apparaître à gauche ou à droite d’une assignation.In C++, an lvalue can appear on the left or on the right of an assignment. Le « l » dans « lvalues » ne vous permet pas vraiment de comprendre ni de définir ce qu’elles sont.The "l" in "lvalues", then, doesn't actually help you to comprehend nor define what they are. Vous devez seulement comprendre que ce que nous appelons une lvalue est une valeur qui a une identité.You need only to understand that what we call an lvalue is a value that has identity.

Une variable ou une constante nommée, ou encore une fonction qui retourne une référence constituent des exemples d’expressions qui sont des lvalues.Examples of expressions that are lvalues include: a named variable or constant; or a function that returns a reference. Un élément temporaire ou une fonction qui produit un retour par valeur constituent des exemples d’expressions qui en sont pas des lvalues.Examples of expressions that are not lvalues include: a temporary; or a function that returns by value.

int& get_by_ref() { ... }
int get_by_val() { ... }

int main()
{
    std::vector<byte> vec{ 99, 98, 97 };
    std::vector<byte>* addr1{ &vec }; // ok: vec is an lvalue.
    int* addr2{ &get_by_ref() }; // ok: get_by_ref() is an lvalue.

    int* addr3{ &(get_by_ref() + 1) }; // Error: get_by_ref() + 1 is not an lvalue.
    int* addr4{ &get_by_val() }; // Error: get_by_val() is not an lvalue.
}

Maintenant, même s’il est vrai que les lvalues ont une identité, c’est également le cas des xvalues.Now, while it's a true statement that lvalues have identity, so do xvalues. Nous examinerons plus en détail ce qu’est une xvalue plus loin dans cette rubrique.We'll go more into what an xvalue is later in this topic. Pour le moment, sachez simplement qu’il existe une catégorie de valeurs appelée glvalue, pour « generalized lvalue » (lvalue généralisée).For now, just be aware that there is a value category called glvalue, for "generalized lvalue". Le sur-ensemble des glvalues contient à la fois les lvalues (également appelées lvalues classiques) et les xvalues.The superset of glvalues contains both lvalues (also known as classical lvalues) and xvalues. Par conséquent, même s’il est vrai qu’« une lvalue a une identité », l’ensemble complet des éléments ayant une identité est l’ensemble des glvalues, comme indiqué dans cette illustration.So, while "an lvalue has identity" is true, the complete set of things that have identity is the set of glvalues, as shown in this illustration.

Une lvalue a une identité

Une rvalue est déplaçable ; une lvalue ne l’est pasAn rvalue is movable; an lvalue is not

Il y a toutefois des valeurs qui ne sont pas des glvalues.But there are values that are not glvalues. Par conséquent, il existe des valeurs pour lesquelles vous ne pouvez pas obtenir une adresse de mémoire (ou sur la validité desquelles vous ne pouvez pas compter).Consequently, there are values that you can't obtain a memory address for (or you can't rely on it to be valid). Nous avons vu de telles valeurs dans l’exemple de code ci-dessus.We saw some such values in the code example above. Cela peut paraître un inconvénient.This sounds like a disadvantage. Mais, en réalité, l’avantage d’une valeur comme celle-ci est que vous pouvez vous déplacer à partir de celle-ci (ce qui est généralement économique), au lieu de copier à partir de celle-ci (ce qui est généralement coûteux).But in fact the advantage of a value like that is that you can move from it (which is generally cheap), rather than copy from it (which is generally expensive). Un déplacement à partir d’une valeur signifie qu’elle n’est plus à l’emplacement où elle se trouvait avant.Moving from a value means that it's no longer in the place it used to be. Par conséquent, il faut éviter de tenter d’y accéder à cet emplacement.So, trying to access it in the place it used to be is something to be avoided. Quand et comment déplacer une valeur n’est pas abordé dans cette rubrique.A discussion of when and how to move a value is out of scope for this topic. Dans cette rubrique, nous devons juste savoir qu’une valeur qui peut être déplacée est appelée rvalue (ou rvalue classique).For this topic, we just need to know that a value that is movable is known as an rvalue (or classical rvalue).

Le « r » dans « rvalue » est l’abréviation de « right » (droite) comme dans le côté droit d’une assignation).The "r" in "rvalue" is an abbreviation of "right" (as in, the right-hand-side of an assignment). Mais vous pouvez utiliser des rvalues et des références à des rvalues en dehors des affectations.But you can use rvalues, and references to rvalues, outside of assignments. Le « r » dans « rvalues » n’est donc pas le point sur lequel il faut se concentrer.The "r" in "rvalues", then, is not the thing to focus on. Vous devez seulement comprendre que ce que nous appelons une rvalue est une valeur qui est déplaçable.You need only to understand that what we call an rvalue is a value that is movable.

À l’inverse, une lvalue n’est pas déplaçable, comme le montre cette illustration.An lvalue, conversely, isn't movable, as shown in this illustration. Une lvalue déplacée défierait la définition d’une lvalue, ce qui constituerait un problème inattendu pour le code qui, de façon très raisonnable, était supposé pouvoir continuer à accéder à la lvalue.An lvalue that moved would defy the definition of lvalue, and it would be an unexpected problem for code that very reasonably expected to be able to continue to access the lvalue.

Une rvalue est déplaçable ; une lvalue ne l’est pas

Vous ne pouvez pas déplacer une lvalue.You can't move an lvalue. Mais il existe une sorte de glvalue (l’ensemble d’éléments ayant une identité) que vous pouvez déplacer, si vous savez ce que vous faites (en veillant notamment à ne pas y accéder après le déplacement) : il s’agit de la xvalue.But there is a kind of glvalue (the set of things with identity) that you can move—if you know what you're doing (including being careful not to access it after the move)—and that's the xvalue. Nous reviendrons à ce concept une fois ci-dessous, quand nous examinerons la vue complète des catégories de valeurs.We'll revisit this idea one more time below, when we look at the complete picture of value categories.

Références rvalue et règles de liaison des référencesRvalue references, and reference-binding rules

Cette section présente la syntaxe d’une référence à une rvalue.This section introduces the syntax for a reference to an rvalue. Nous allons devoir attendre une autre rubrique pour traiter de manière substantielle le déplacement et le transfert, mais pour l’instant sachez que les références aux rvalue sont nécessaires à la solution de ces problèmes.We'll have to wait for another topic to go into a substantial treatment of moving and forwarding, but suffice to say that rvalue references are a necessary piece of the solution of those problems. Toutefois, avant d’étudier les références rvalue, nous devons d’abord clarifier ce qu’est T&, l’élément que nous avons auparavant simplement appelé « référence ».Before we look at rvalue references, though, we first need to be clearer about T&—the thing we've formerly been calling just "a reference". Il s’agit réellement d’« une référence lvalue (non-const) », qui fait référence à une valeur à laquelle l’utilisateur de la référence peut écrire.It's really "an lvalue (non-const) reference", which refers to an value to which the user of the reference can write.

template<typename T> T& get_by_lvalue_ref() { ... } // Get by lvalue (non-const) reference.
template<typename T> void set_by_lvalue_ref(T&) { ... } // Set by lvalue (non-const) reference.

Une référence lvalue peut établir une liaison à une lvalue, mais pas à une rvalue.An lvalue reference can bind to an lvalue, but not to an rvalue.

Ensuite, il existe des références const lvalue (T const&), qui font référence à des objets auxquels l’utilisateur de la référence ne peut pas écrire (par exemple, une constante).Then there are lvalue const references (T const&), which refer to objects to which the user of the reference can't write (for example, a constant).

template<typename T> T const& get_by_lvalue_cref() { ... } // Get by lvalue const reference.
template<typename T> void set_by_lvalue_cref(T const&) { ... } // Set by lvalue const reference.

Une référence const lvalue peut établir une liaison à une lvalue ou à une rvalue.An lvalue const reference can bind to an lvalue or to an rvalue.

La syntaxe d’une référence à une rvalue de type T est écrite sous la forme T&&.The syntax for a reference to an rvalue of type T is written as T&&. Une référence rvalue fait référence à une valeur déplaçable, c’est-à-dire une valeur dont que nous n’avons pas besoin de conserver le contenu une fois que nous l’avons utilisée (par exemple, un élément temporaire).An rvalue reference refers to a movable value—an value whose contents we don't need to preserve after we've used it (for example, a temporary). Étant donné que l’objectif principal est de se déplacer à partir la valeur liée à une référence rvalue (en la modifiant donc), les qualificateurs const et volatile (également appelés « qualificateurs cv ») ne s’appliquent pas aux références rvalue.Since the whole point is to move from (thereby modifying) the value bound to an rvalue reference, const and volatile qualifiers (also known as cv-qualifiers) don't apply to rvalue references.

template<typename T> T&& get_by_rvalue_ref() { ... } // Get by rvalue reference.
struct A { A(A&& other) { ... } }; // A move constructor takes an rvalue reference.

Une référence rvalue établit une liaison à une rvalue.An rvalue reference binds to an rvalue. En réalité, en termes de résolution de surcharge, une rvalue préfère être liée à une référence rvalue plutôt qu’à une référence const lvalue.In fact, in terms of overload resolution, an rvalue prefers to be bound to an rvalue reference than to an lvalue const reference. Mais une référence rvalue ne peut pas établir une liaison à une lvalue car, comme nous l’avons dit, une référence rvalue fait référence à une valeur dont on présume que nous n’avons pas besoin de conserver le contenu (par exemple, le paramètre d’un constructeur de déplacement).But an rvalue reference can't bind to an lvalue because, as we've said, an rvalue reference refers to a value whose contents it's assumed we don't need to preserve (say, the parameter for a move constructor).

Vous pouvez également transmettre une rvalue où un argument par valeur est attendu, par le biais d’une construction de copie (ou par le biais d’une construction de déplacement, si la rvalue est une xvalue).You can also pass an rvalue where a by-value argument is expected, via copy construction (or via move construction, if the rvalue is an xvalue).

Une glvalue a une identité ; une prvalue n’en a pasA glvalue has identity; a prvalue does not

À ce stade, nous savons ce qu’est une identité.At this stage, we know what has identity. Et nous savons ce qui est déplaçable et ce qui ne l’est pas.And we know what's movable and what isn't. Mais nous n’avons pas encore nommé l’ensemble des valeurs qui n’ontpas d’identité.But we haven't yet named the set of values that don't have identity. Cet ensemble est appelé prvalue, ou rvalue pure.That set is known as the prvalue, or pure rvalue.

int& get_by_ref() { ... }
int get_by_val() { ... }

int main()
{
    int* addr3{ &(get_by_ref() + 1) }; // Error: get_by_ref() + 1 is a prvalue.
    int* addr4{ &get_by_val() }; // Error: get_by_val() is a prvalue.
}

Une lvalue a une identité ; une prvalue n’en a pas

Vue complète des catégories de valeursThe complete picture of value categories

Il reste uniquement à regrouper les informations et les illustrations ci-dessus en une seule vue complète.It only remains to combine the info and illustrations above into a single, big picture.

Vue complète des catégories de valeurs

glvalue (i)glvalue (i)

Une glvalue (lvalue généralisée) a une identité.A glvalue (generalized lvalue) has identity.

lvalue (i&!m)lvalue (i&!m)

Une lvalue (sorte de glvalue) a une identité, mais ne peut pas être déplacée.An lvalue (a kind of glvalue) has identity, but isn't movable. Il s’agit de valeurs généralement en lecture-écriture que vous passez par référence ou par référence const, ou encore par valeur si la copie est économique.These are typically read-write values that you pass around by reference or by const reference, or by value if copying is cheap. Une lvalue ne peut pas être liée à une référence rvalue.An lvalue can't be bound to an rvalue reference.

xvalue (i&m)xvalue (i&m)

Une xvalue (sorte de glvalue, mais également sorte de rvalue) a une identité et peut aussi être déplacée.An xvalue (a kind of glvalue, but also a kind of rvalue) has identity, and is also movable. Ce peut être une ancienne lvalue que vous avez décidé de déplacer car la copie est coûteuse, et vous veillerez à ne pas y accéder par la suite.This might be an erstwhile lvalue that you've decided to move because copying is expensive, and you'll be careful not to access it afterward. Voici comment vous pouvez transformer une lvalue en xvalue.Here's how you can turn an lvalue into an xvalue.

struct A { ... };
A a; // a is an lvalue...
static_cast<A&&>(a); // ...but this expression is an xvalue.

Dans l’exemple de code ci-dessus, nous n’avons encore rien déplacé.In the code example above, we haven't moved anything yet. Nous avons juste créé une xvalue en effectuant un cast d’une lvalue en référence rvalue sans nom.We've just created an xvalue by casting an lvalue to an unnamed rvalue reference. Elle peut toujours être identifiée par son nom d’lvalue ; mais en tant que xvalue, elle est désormais capable d’être déplacée.It can still be identified by its lvalue name; but, as an xvalue, it is now capable of being moved. Les raisons de le faire et ce à quoi ressemble un déplacement réellement feront l’objet d’une autre rubrique.The reasons for doing so, and what moving actually looks like, will have to wait for another topic. Mais si cela vous aide, vous pouvez considérer que le « x » de « xvalue » signifie « expert seulement ».But you can think of the "x" in "xvalue" as meaning "expert-only" if that helps. En effectuant un cast d’une lvalue en xvalue (sorte de rvalue), la valeur devient capable d’être liée à une référence rvalue.By casting an lvalue into an xvalue (a kind of rvalue), the value then becomes capable of being bound to an rvalue reference.

Voici deux autres exemples de xvalues : un appelant une fonction qui retourne une référence rvalue sans nom et un accédant à un membre d’une xvalue.Here are two other examples of xvalues—calling a function that returns an unnamed rvalue reference, and accessing a member of an xvalue.

struct A { int m; };
A&& f();
f(); // This expression is an xvalue...
f().m; // ...and so is this.

prvalue (!i&m)prvalue (!i&m)

Une prvalue (rvalue pure ; sorte de rvalue) n’a pas d’identité, mais est déplaçable.A prvalue (pure rvalue; a kind of rvalue) doesn't have identity, but is movable. Il s’agit généralement d’éléments temporaires, le résultat de l’appel d’une fonction qui retourne par valeur ou le résultat de l’évaluation de toute autre expression qui n’est pas une glvalue.These are typically temporaries, the result of calling a function that returns by value, or the result of evaluating any other expression that's not a glvalue,

rvalue (m)rvalue (m)

Une rvalue est déplaçable.An rvalue is movable. Une référence rvalue fait toujours référence à une rvalue (valeur dont on présume que nous n’avons pas besoin de conserver le contenu).An rvalue reference always refers to an rvalue (a value whose contents it's assumed we don't need to preserve).

Mais une référence rvalue est-elle elle-même une rvalue ?But, is an rvalue reference itself an rvalue? Un référence rvalue sans nom (comme celles indiquées dans les exemples de code xvalue ci-dessus) est une xvalue. Donc, oui, il s’agit d’une rvalue.An unnamed rvalue reference (like the ones shown in the xvalue code examples above) is an xvalue so, yes, it's an rvalue. Elle préfère être liée à un paramètre de fonction de référence rvalue, comme celle d’un constructeur de déplacement.It prefers to be bound to an rvalue reference function parameter, such as that of a move constructor. À l’inverse (et peut-être contre toute attente), si une référence rvalue a un nom, l’expression composée de ce nom est une lvalue.Conversely (and perhaps counter-intuitively), if an rvalue reference has a name, then the expression consisting of that name is an lvalue. Par conséquent, elle ne peut pas être liée à un paramètre de référence rvalue.So it can't be bound to an rvalue reference parameter. Mais il est facile de faire en sorte qu’elle le soit : il vous suffit d’en effectuer à nouveau un cast en une référence rvalue sans nom (une xvalue).But it's easy to make it do so—just cast it to an unnamed rvalue reference (an xvalue) again.

void foo(A&) { ... }
void foo(A&&) { ... }
void bar(A&& a) // a is a named rvalue reference; it's an lvalue.
{
    foo(a); // Calls foo(A&).
    foo(static_cast<A&&>(a)); // Calls foo(A&&).
}
A&& get_by_rvalue_ref() { ... } // This unnamed rvalue reference is an xvalue.

!i&!m!i&!m

Le type de valeur qui n’a pas identité et qui n’est pas déplaçable est la combinaison que nous n’avons pas encore abordée.The kind of value that doesn't have identity and isn't movable is the one combination that we haven't yet discussed. Mais nous pouvons l’ignorer, car cette catégorie n’est pas un concept utile en langage C++.But we can disregard it, because that category isn't a useful idea in the C++ language.

Règles de réduction de référenceReference-collapsing rules

Plusieurs références similaires dans une expression (une référence lvalue à une référence lvalue, ou une référence rvalue à une référence rvalue) s’annulent mutuellement.Multiple like references in an expression (an lvalue reference to an lvalue reference, or an rvalue reference to an rvalue reference) cancel one another out.

  • A& & est réduite en A&.A& & collapses into A&.
  • A&& && est réduite en A&&.A&& && collapses into A&&.

Plusieurs références différentes dans une expression sont réduites en une référence lvalue.Multiple unlike references in an expression collapse to an lvalue reference.

  • A& && est réduite en A&.A& && collapses into A&.
  • A&& & est réduite en A&.A&& & collapses into A&.

Références de transfertForwarding references

Cette dernière section compare des références rvalue, que nous avons déjà présentées, avec le nouveau concept de référence de transfert.This final section contrasts rvalue references, which we've already discussed, with the different concept of a forwarding reference.

void foo(A&& a) { ... }
  • A&& est une référence rvalue, comme nous l’avons vu.A&& is an rvalue reference, as we've seen. Const et volatile ne s’appliquent pas aux références rvalue.Const and volatile don't apply to rvalue references.
  • foo accepte uniquement des rvalues de type A.foo accepts only rvalues of type A.
  • La raison de l’existence de références rvalue (comme A&&) est de vous permettre de créer une autre surcharge qui est optimisée en cas de passage d’un élément temporaire (ou d’une autre rvalue).The reason rvalue references (such as A&&) exist is so that you can author an overload that's optimized for the case of a temporary (or other rvalue) being passed.
template <typename _Ty> void bar(_Ty&& ty) { ... }
  • _Ty&& est une référence de transfert._Ty&& is a forwarding reference. En fonction de ce que vous passez à bar, le type  _Ty peut être const/non-const indépendamment de volatile/non-volatile.Depending what you pass to bar, type _Ty could be const/non-const independently of volatile/non-volatile.
  • bar accepte n’importe quelle lvalue ou rvalue de type  _Ty.bar accepts any lvalue or rvalue of type _Ty.
  • Si une lvalue est passée, la référence de transfert devient _Ty& &&, qui est réduite en référence lvalue _Ty&.Passing an lvalue causes the forwarding reference to become _Ty& &&, which collapses to the lvalue reference _Ty&.
  • Si une rvalue est passée, la référence de transfert devient _Ty&& &&, qui est réduite en référence rvalue _Ty&&.Passing an rvalue causes the forwarding reference to become _Ty&& &&, which collapses to the rvalue reference _Ty&&.
  • La raison de l’existence de références de transfert (comme _Ty&&) n’est pas l’optimisation, mais de prendre ce que vous leur passez et de le transférer de façon transparente et efficace.The reason forwarding references (such as _Ty&&) exist is not for optimization, but to take what you pass to them and to forward it on transparently and efficiently. Vous rencontrerez probablement une référence de transfert uniquement si vous écrivez (ou étudiez attentivement) le code de la bibliothèque ; par exemple, une fonction d’usine qui effectue un transfert sur des arguments de constructeur.You're likely to encounter a forwarding reference only if you write (or closely study) library code—for example, a factory function that forwards on constructor arguments.

SourcesSources

  • [Stroustrup, 2013] B. Stroustrup: The C++ Programming Language, Fourth Edition.[Stroustrup, 2013] B. Stroustrup: The C++ Programming Language, Fourth Edition. Addison-Wesley.Addison-Wesley. 2013.2013.