Namespaces
Variants

Value categories

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Value categories
Order of evaluation
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Chaque expression C++ (un opérateur avec ses opérandes, un littéral, un nom de variable, etc.) est caractérisée par deux propriétés indépendantes : un type et une catégorie de valeur . Chaque expression possède un type non-référence, et chaque expression appartient exactement à l'une des trois catégories de valeur primaires : prvalue , xvalue , et lvalue .

  • un glvalue (lvalue « généralisé ») est une expression dont l'évaluation détermine l'identité d'un objet ou d'une fonction ;
  • un prvalue (rvalue « pur ») est une expression dont l'évaluation
  • calcule la valeur d'un opérande d'un opérateur intégré (une telle prvalue n'a pas d'objet résultat ), ou
  • initialise un objet (une telle prvalue est dite avoir un objet résultat ).
L'objet résultat peut être une variable, un objet créé par new-expression , un temporaire créé par materialisation temporaire , ou un membre de ceux-ci. Notez que les expressions non- void rejetées ont un objet résultat (le temporaire matérialisé). De plus, toute prvalue de classe et de tableau a un objet résultat sauf lorsqu'elle est l'opérande de decltype ;
  • un xvalue (une valeur « eXpiring ») est une glvalue qui désigne un objet dont les ressources peuvent être réutilisées ;
  • un lvalue est une glvalue qui n'est pas un xvalue ;
Contenu étendu

Appelés ainsi historiquement car les lvalues pouvaient apparaître à gauche d'une expression d'assignation. En général, ce n'est pas toujours le cas :

void foo();
void baz()
{
    int a; // Expression `a` is lvalue
    a = 4; // OK, could appear on the left-hand side of an assignment expression
    int &b{a}; // Expression `b` is lvalue
    b = 5; // OK, could appear on the left-hand side of an assignment expression
    const int &c{a}; // Expression `c` is lvalue
    c = 6;           // ill-formed, assignment of read-only reference
    // Expression `foo` is lvalue
    // address may be taken by built-in address-of operator
    void (*p)() = &foo;
    foo = baz; // ill-formed, assignment of function
}
  • une rvalue est une prvalue ou une xvalue ;
Contenu étendu

Appelés ainsi historiquement car les rvalues pouvaient apparaître sur le côté droit d'une expression d'affectation. En général, ce n'est pas toujours le cas :

#include <iostream>
struct S
{
    S() : m{42} {}
    S(int a) : m{a} {}
    int m;
};
int main()
{
    S s;
    // Expression `S{}` is prvalue
    // May appear on the right-hand side of an assignment expression
    s = S{};
    std::cout << s.m << '\n';
    // Expression `S{}` is prvalue
    // Can be used on the left-hand side too
    std::cout << (S{} = S{7}).m << '\n';
}

Sortie :

42
7

Note : cette taxonomie a subi des changements significatifs avec les révisions passées de la norme C++, voir Historique ci-dessous pour plus de détails.

Contenu étendu

Malgré leurs noms, ces termes classifient les expressions, pas les valeurs.

#include <type_traits>
#include <utility>
template <class T> struct is_prvalue : std::true_type {};
template <class T> struct is_prvalue<T&> : std::false_type {};
template <class T> struct is_prvalue<T&&> : std::false_type {};
template <class T> struct is_lvalue : std::false_type {};
template <class T> struct is_lvalue<T&> : std::true_type {};
template <class T> struct is_lvalue<T&&> : std::false_type {};
template <class T> struct is_xvalue : std::false_type {};
template <class T> struct is_xvalue<T&> : std::false_type {};
template <class T> struct is_xvalue<T&&> : std::true_type {};
int main()
{
    int a{42};
    int& b{a};
    int&& r{std::move(a)};
    // Expression `42` is prvalue
    static_assert(is_prvalue<decltype((42))>::value);
    // Expression `a` is lvalue
    static_assert(is_lvalue<decltype((a))>::value);
    // Expression `b` is lvalue
    static_assert(is_lvalue<decltype((b))>::value);
    // Expression `std::move(a)` is xvalue
    static_assert(is_xvalue<decltype((std::move(a)))>::value);
    // Type of variable `r` is rvalue reference
    static_assert(std::is_rvalue_reference<decltype(r)>::value);
    // Type of variable `b` is lvalue reference
    static_assert(std::is_lvalue_reference<decltype(b)>::value);
    // Expression `r` is lvalue
    static_assert(is_lvalue<decltype((r))>::value);
}

Table des matières

Catégories principales

lvalue

Les expressions suivantes sont des expressions lvalue :

Contenu étendu
void foo() {}
void baz()
{
    // `foo` est une lvalue
    // l'adresse peut être prise par l'opérateur d'adresse intégré
    void (*p)() = &foo;
}
struct foo {};
template <foo a>
void baz()
{
    const foo* obj = &a;  // `a` est une lvalue, objet paramètre de template
}
  • un appel de fonction ou une expression d'opérateur surchargé, dont le type de retour est une référence lvalue, tel que std:: getline ( std:: cin , str ) , std:: cout << 1 , str1 = str2 , ou ++ it ;
Contenu étendu
int& a_ref()
{
    static int a{3};
    return a;
}
void foo()
{
    a_ref() = 5;  // `a_ref()` est une lvalue, appel de fonction dont le type de retour est une référence lvalue
}
Contenu étendu
struct foo
{
    enum bar
    {
        m // membre énumérateur
    };
};
void baz()
{
    foo a;
    a.m = 42; // mal formé, lvalue requis comme opérande gauche d'assignation
}
struct foo
{
    void m() {} // fonction membre non statique
};
void baz()
{
    foo a;
    // `a.m` est une prvalue, donc l'adresse ne peut pas être prise par l'opérateur
    // d'adresse intégré
    void (foo::*p1)() = &a.m; // mal formé
    void (foo::*p2)() = &foo::m; // OK : pointeur vers fonction membre
}
struct foo
{
    static void m() {} // fonction membre statique
};
void baz()
{
    foo a;
    void (*p1)() = &a.m;     // `a.m` est une lvalue
    void (*p2)() = &foo::m;  // identique
}
template <int& v>
void set()
{
    v = 5; // le paramètre template est une lvalue
}
int a{3}; // variable statique, adresse fixe connue à la compilation
void foo()
{
    set<a>();
}
  • un appel de fonction ou une expression d'opérateur surchargé, dont le type de retour est une référence rvalue vers une fonction ;
  • une expression de cast vers un type de référence rvalue vers fonction, telle que static_cast < void ( && ) ( int ) > ( x ) .
(depuis C++11)

Propriétés :

  • Identique à glvalue (ci-dessous).
  • L'adresse d'une lvalue peut être prise par l'opérateur d'adresse intégré : & ++ i [1] et & std:: hex sont des expressions valides.
  • Une lvalue modifiable peut être utilisée comme opérande gauche des opérateurs d'affectation et d'affectation composée intégrés.
  • Une lvalue peut être utilisée pour initialiser une référence lvalue ; cela associe un nouveau nom à l'objet identifié par l'expression.

prvalue

Les expressions suivantes sont des expressions prvalue :

template <int v>
void foo()
{
    // pas une lvalue, `v` est un paramètre de template de type scalaire int
    const int* a = &v; // mal formé
    v = 3; // mal formé : lvalue requise comme opérande gauche d'assignation
}
(depuis C++11)
(depuis C++20)

Propriétés :

  • Identique à rvalue (ci-dessous).
  • Une prvalue ne peut pas être polymorphe : le type dynamique de l'objet qu'elle désigne est toujours le type de l'expression.
  • Une prvalue non-classe non-tableau ne peut pas être cv-qualifiée , sauf si elle est matérialisée pour être liée à une référence vers un type cv-qualifié (depuis C++17) . (Note : un appel de fonction ou une conversion peut produire une prvalue de type non-classe cv-qualifié, mais le qualificatif cv est généralement immédiatement supprimé.)
  • Une prvalue ne peut pas avoir de type incomplet (sauf pour le type void , voir ci-dessous, ou lorsqu'elle est utilisée dans un spécificateur decltype ).
  • Une prvalue ne peut pas avoir de type classe abstraite ou de tableau de celui-ci.

xvalue

Les expressions suivantes sont des expressions xvalue :

  • un appel de fonction ou une expression d'opérateur surchargé, dont le type de retour est une référence à une valeur rvalue vers un objet, tel que std :: move ( x ) ;
  • a [ n ] , l'expression d'indice intégrée , où un opérande est une valeur rvalue de tableau ;
  • une expression de conversion vers un type référence à une valeur rvalue vers un objet, telle que static_cast < char && > ( x ) ;
(depuis C++11)
(depuis C++17)
(depuis C++23)

Propriétés :

  • Identique à rvalue (ci-dessous).
  • Identique à glvalue (ci-dessous).

En particulier, comme toutes les rvalues, les xvalues se lient aux références de rvalue, et comme toutes les glvalues, les xvalues peuvent être polymorphes , et les xvalues non-classes peuvent être qualifiées cv .

Contenu étendu
#include <type_traits>
template <class T> struct is_prvalue : std::true_type {};
template <class T> struct is_prvalue<T&> : std::false_type {};
template <class T> struct is_prvalue<T&&> : std::false_type {};
template <class T> struct is_lvalue : std::false_type {};
template <class T> struct is_lvalue<T&> : std::true_type {};
template <class T> struct is_lvalue<T&&> : std::false_type {};
template <class T> struct is_xvalue : std::false_type {};
template <class T> struct is_xvalue<T&> : std::false_type {};
template <class T> struct is_xvalue<T&&> : std::true_type {};
// Exemple tiré de la norme C++23 : 7.2.1 Catégorie de valeur [basic.lval]
struct A
{
    int m;
};
A&& operator+(A, A);
A&& f();
int main()
{
    A a;
    A&& ar = static_cast<A&&>(a);
    // Appel de fonction avec type de retour référence rvalue est xvalue
    static_assert(is_xvalue<decltype( (f()) )>::value);
    // Membre d'une expression objet, l'objet est xvalue, `m` est un membre de données non statique
    static_assert(is_xvalue<decltype( (f().m) )>::value);
    // Expression de cast vers référence rvalue
    static_assert(is_xvalue<decltype( (static_cast<A&&>(a)) )>::value);
    // Expression d'opérateur, dont le type de retour est référence rvalue vers objet
    static_assert(is_xvalue<decltype( (a + a) )>::value);
    // L'expression `ar` est lvalue, `&ar` est valide
    static_assert(is_lvalue<decltype( (ar) )>::value);
    [[maybe_unused]] A* ap = &ar;
}

Catégories mixtes

glvalue

Une expression glvalue est soit une lvalue, soit une xvalue.

Propriétés :

  • Une glvalue peut être implicitement convertie en prvalue avec une conversion implicite lvalue-à-rvalue, tableau-à-pointeur, ou fonction-à-pointeur conversion implicite .
  • Une glvalue peut être polymorphe : le type dynamique de l'objet qu'elle identifie n'est pas nécessairement le type statique de l'expression.
  • Une glvalue peut avoir un type incomplet , lorsque cela est permis par l'expression.

rvalue

Une expression rvalue est soit une prvalue soit une xvalue.

Propriétés :

  • L'adresse d'une rvalue ne peut pas être prise par l'opérateur d'adresse intégré : & int ( ) , & i ++ [3] , & 42 , et & std :: move ( x ) sont invalides.
  • Une rvalue ne peut pas être utilisée comme opérande gauche des opérateurs d'assignation ou d'assignation composée intégrés.
  • Une rvalue peut être utilisée pour initialiser une référence lvalue const , auquel cas la durée de vie de l'objet temporaire identifié par la rvalue est étendue jusqu'à la fin de la portée de la référence.
  • Une rvalue peut être utilisée pour initialiser une référence à une rvalue , auquel cas la durée de vie de l'objet temporaire identifié par la rvalue est étendue jusqu'à la fin de la portée de la référence.
  • Lorsqu'elle est utilisée comme argument de fonction et que deux surcharges de la fonction sont disponibles, l'une prenant un paramètre de référence à une rvalue et l'autre prenant une référence à une lvalue vers un paramètre const, une rvalue se lie à la surcharge de référence à une rvalue (ainsi, si les constructeurs de copie et de déplacement sont tous deux disponibles, un argument rvalue invoque le constructeur de déplacement , et de même avec les opérateurs d'affectation de copie et de déplacement).
(depuis C++11)

Catégories spéciales

Appel de fonction membre en attente

Les expressions a. mf et p - > mf , où mf est une fonction membre non statique , et les expressions a. * pmf et p - > * pmf , où pmf est un pointeur vers une fonction membre , sont classées comme des expressions prvalue, mais elles ne peuvent pas être utilisées pour initialiser des références, comme arguments de fonction, ou à aucune autre fin, sauf comme opérande gauche de l'opérateur d'appel de fonction, par exemple ( p - > * pmf ) ( args ) .

Expressions de type void

Les expressions d'appel de fonction retournant void , les expressions de cast vers void , et les expressions throw sont classées comme expressions prvalue, mais elles ne peuvent pas être utilisées pour initialiser des références ou comme arguments de fonction. Elles peuvent être utilisées dans des contextes à valeur ignorée (par exemple sur une ligne seule, comme opérande gauche de l'opérateur virgule, etc.) et dans l'instruction return d'une fonction retournant void . De plus, les expressions throw peuvent être utilisées comme deuxième et troisième opérandes de l' opérateur conditionnel ?: .

Les expressions de type void n'ont pas d' objet résultat .

(since C++17)

Champs de bits

Une expression qui désigne un bit-field (par exemple a. m , où a est une lvalue de type struct A { int m : 3 ; } ) est une expression glvalue : elle peut être utilisée comme opérande gauche de l'opérateur d'affectation, mais son adresse ne peut pas être prise et une référence non-const lvalue ne peut pas lui être liée. Une référence const lvalue ou une référence rvalue peut être initialisée à partir d'une glvalue de bit-field, mais une copie temporaire du bit-field sera créée : elle ne se liera pas directement au bit-field.

Expressions éligibles au déplacement

Bien qu'une expression constituée du nom d'une variable soit une expression lvalue, une telle expression peut être éligible au déplacement si elle apparaît comme opérande de

Si une expression est éligible au déplacement, elle est traitée soit comme une rvalue soit comme une lvalue (jusqu'à C++23) comme une rvalue (depuis C++23) pour la résolution de surcharge (elle peut donc sélectionner le constructeur de déplacement ). Voir Déplacement automatique depuis les variables locales et paramètres pour plus de détails.

(depuis C++11)

Historique

CPL

Le langage de programmation CPL fut le premier à introduire les catégories de valeur pour les expressions : toutes les expressions CPL peuvent être évaluées en "mode droite", mais seuls certains types d'expressions sont significatifs en "mode gauche". Lorsqu'elle est évaluée en mode droite, une expression est considérée comme étant une règle pour le calcul d'une valeur (la valeur droite, ou rvalue ). Lorsqu'elle est évaluée en mode gauche, une expression donne effectivement une adresse (la valeur gauche, ou lvalue ). "Gauche" et "Droite" représentaient ici "à gauche de l'affectation" et "à droite de l'affectation".

C

Le langage de programmation C a suivi une taxonomie similaire, sauf que le rôle de l'affectation n'était plus significatif : les expressions C sont catégorisées entre "expressions lvalue" et autres (fonctions et valeurs non-objets), où "lvalue" signifie une expression qui identifie un objet, une "valeur localisateur" [4] .

C++98

Le C++ d'avant 2011 suivait le modèle C, mais a restauré le nom "rvalue" pour les expressions non-lvalue, a transformé les fonctions en lvalues, et a ajouté la règle que les références peuvent se lier aux lvalues, mais seules les références vers const peuvent se lier aux rvalues. Plusieurs expressions C non-lvalue sont devenues des expressions lvalue en C++.

C++11

Avec l'introduction de la sémantique de déplacement en C++11, les catégories de valeur ont été redéfinies pour caractériser deux propriétés indépendantes des expressions [5] :

  • a une 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 des fonctions qu'elles identifient (obtenues directement ou indirectement) ;
  • peut être déplacé : move constructor , move assignment operator , ou une autre surcharge de fonction implémentant la sémantique de déplacement peut se lier à l'expression.

En C++11, les expressions qui :

  • ont une identité et ne peuvent pas être déplacés 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 sont appelés prvalue ("pure rvalue") expressions ;
  • n'ont pas d'identité et ne peuvent pas être déplacés ne sont pas utilisés [6] .

Les expressions qui ont une identité sont appelées "expressions glvalue" (glvalue signifie "lvalue généralisée"). Les lvalues et les xvalues sont toutes deux des expressions glvalue.

Les expressions qui peuvent être déplacées sont appelées « expressions rvalue ». Les prvalues et les xvalues sont toutes deux des expressions rvalue.

C++17

En C++17, l'élision de copie est devenue obligatoire dans certaines situations, ce qui a nécessité la séparation des expressions prvalue des objets temporaires initialisés par celles-ci, aboutissant au système que nous avons aujourd'hui. Notez que, contrairement au schéma C++11, les prvalues ne sont plus déplacées.

Notes de bas de page

  1. En supposant que i ait un type natif ou que l'opérateur de pré-incrémentation soit surchargé pour renvoyer par référence lvalue.
  2. 2.0 2.1 2.2 2.3 Catégorie rvalue spéciale, voir appel de fonction membre en attente .
  3. En supposant que i ait un type natif ou que l'opérateur de post-incrémentation ne soit pas surchargé pour renvoyer par référence lvalue.
  4. "Une divergence d'opinion au sein de la communauté C concernait la signification de lvalue, un groupe considérant une lvalue comme tout type de localisateur d'objet, un autre groupe estimant qu'une lvalue est significative du côté gauche d'un opérateur d'affectation. Le comité C89 a adopté la définition de lvalue comme localisateur d'objet." -- ANSI C Rationale, 6.3.2.1/10.
  5. Terminologie des valeurs "nouvelle" par Bjarne Stroustrup, 2010.
  6. Les prvalues constantes (autorisées uniquement pour les types classe) et les xvalues constantes ne se lient pas aux surcharges T&& , mais elles se lient aux surcharges const T && , qui sont également classées comme "constructeur de déplacement" et "opérateur d'affectation de déplacement" par la norme, satisfaisant la définition de "pouvant être déplacé" aux fins de cette classification. Cependant, de telles surcharges ne peuvent pas modifier leurs arguments et ne sont pas utilisées en pratique ; en leur absence, les prvalues constantes et les xvalues constantes se lient aux surcharges const T & .

Références

  • Norme C++23 (ISO/IEC 14882:2024) :
  • 7.2.1 Catégorie de valeur [basic.lval]
  • Norme C++20 (ISO/CEI 14882:2020) :
  • 7.2.1 Catégorie de valeur [basic.lval]
  • Norme C++17 (ISO/CEI 14882:2017) :
  • 6.10 Lvalues et rvalues [basic.lval]
  • Norme C++14 (ISO/CEI 14882:2014) :
  • 3.10 Lvalues et rvalues [basic.lval]
  • Norme C++11 (ISO/IEC 14882:2011) :
  • 3.10 Lvalues et rvalues [basic.lval]
  • Norme C++98 (ISO/CEI 14882:1998) :
  • 3.10 Lvalues et rvalues [basic.lval]

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement publié Comportement corrigé
CWG 616 C++11 l'accès membre et l'accès membre via
un pointeur sur membre d'une rvalue donnait une prvalue
reclassifié en xvalue
CWG 1059 C++11 les prvalue de tableau ne pouvaient pas être cv-qualifiées autorisé
CWG 1213 C++11 l'indexation d'une rvalue de tableau donnait une lvalue reclassifiée en xvalue

Voir aussi

Documentation C pour les catégories de valeur

Liens externes

1. Catégories de valeur C++ et decltype démystifiés — David Mazières, 2021
2. Déterminer empiriquement la catégorie de valeur d'une expression — StackOverflow