Value categories
Each C++ expression (an operator with its operands, a literal, a variable name, etc.) is characterized by two independent properties: a type and a value category. Each expression has some non-reference type, and each expression belongs to exactly one of the three primary value categories: prvalue, xvalue, and lvalue
- a glvalue (“generalized” lvalue) is an expression whose evaluation determines the identity of an object or function;
- a prvalue (“pure” rvalue) is an expression whose evaluation
-
- computes the value of an operand of a built-in operator (such prvalue has no result object), or
- initializes an object (such prvalue is said to have a result object).
- The result object may be a variable, an object created by new-expression, a temporary created by temporary materialization, or a member thereof. Note that non-void
discarded
decltype
- an xvalue (an “eXpiring” value) is a glvalue that denotes an object whose resources can be reused;
- an lvalue is a glvalue that is not an xvalue;
Extended content |
---|
So-called, historically, because lvalues could appear on the left-hand side of an assignment expression. In general, it's not always the case: 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 } |
- an rvalue is a prvalue or an xvalue;
Extended content |
---|
So-called, historically, because rvalues could appear on the right-hand side of an assignment expression. In general, it's not always the case: Run this code #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'; } Output: 42 7 |
Note: this taxonomy went through significant changes with past C++ standard revisions, see History below for details.
Extended content |
---|
Despite their names, these terms classify expressions, not values. Run this code #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); } |
Primary categories
lvalue
The following expressions are lvalue expressions:
- the name of a variable, a function , a template parameter object (since C++20) , or a data member, regardless of type, such as std::cin or std::endl . Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression (but see Move-eligible expressions
Extended content |
---|
void foo() {} void baz() { // `foo` is lvalue // address may be taken by built-in address-of operator void (*p)() = &foo; } struct foo {}; template <foo a> void baz() { const foo* obj = &a; // `a` is an lvalue, template parameter object } |
- a function call or an overloaded operator expression, whose return type is lvalue reference, such as std::getline ( std::cin, str) , std::cout << 1, str1 = str2, or ++it
Extended content |
---|
int& a_ref() { static int a{3}; return a; } void foo() { a_ref() = 5; // `a_ref()` is lvalue, function call whose return type is lvalue reference } |
- a = b, a += b, a %= b, and all other built-in assignment and compound assignment
- ++a and --a, the built-in pre-increment and pre-decrement
- *p, the built-in indirection
- a[n] and p[n], the built-in subscript expressions , where one operand in a[n] is an array lvalue (since C++11)
-
a.m, the member of object expression, except where
m
is a member enumerator or a non-static member function, or where a is an rvalue andm
Extended content |
---|
struct foo { enum bar { m // member enumerator }; }; void baz() { foo a; a.m = 42; // ill-formed, lvalue required as left operand of assignment } struct foo { void m() {} // non-static member function }; void baz() { foo a; // `a.m` is a prvalue, hence the address cannot be taken by built-in // address-of operator void (foo::*p1)() = &a.m; // ill-formed void (foo::*p2)() = &foo::m; // OK: pointer to member function } struct foo { static void m() {} // static member function }; void baz() { foo a; void (*p1)() = &a.m; // `a.m` is an lvalue void (*p2)() = &foo::m; // the same } |
-
p->m, the built-in member of pointer expression, except where
m
-
a.*mp, the pointer to member of object expression, where a is an lvalue and
mp
-
p->*mp, the built-in pointer to member of pointer expression, where
mp
- a, b, the built-in comma expression, where b
- a ? b : c, the ternary conditional expression for certain b and c (e.g., when both are lvalues of the same type, but see definition
- a string literal, such as "Hello, world!";
- a cast expression to lvalue reference type, such as static_cast < int & > (x) or static_cast < void ( & ) ( int ) > (x)
- a non-type template parameter of an lvalue reference type;
template <int& v> void set() { v = 5; // template parameter is lvalue } int a{3}; // static variable, fixed address is known at compile-time void foo() { set<a>(); }
|
(since C++11) |
Properties:
- Same as glvalue (below).
- Address of an lvalue may be taken by built-in address-of operator: &++i [1] and &std::endl
- A modifiable lvalue may be used as the left-hand operand of the built-in assignment and compound assignment operators.
- An lvalue may be used to initialize an lvalue reference; this associates a new name with the object identified by the expression.
prvalue
The following expressions are prvalue expressions:
- a literal (except for string literal), such as 42, true or nullptr
- a function call or an overloaded operator expression, whose return type is non-reference, such as str.substr(1, 2), str1 + str2, or it++
- a++ and a--, the built-in post-increment and post-decrement
- a + b, a % b, a & b, a << b, and all other built-in arithmetic
- a && b, a || b, !a, the built-in logical
- a < b, a == b, a >= b, and all other built-in comparison
- &a, the built-in address-of
-
a.m, the member of object expression, where
m
is a member enumerator or a non-static member function[2] -
p->m, the built-in member of pointer expression, where
m
is a member enumerator or a non-static member function[2] -
a.*mp, the pointer to member of object expression, where
mp
is a pointer to member function[2] -
p->*mp, the built-in pointer to member of pointer expression, where
mp
is a pointer to member function[2] - a, b, the built-in comma expression, where b
- a ? b : c, the ternary conditional expression for certain b and c (see definition
- a cast expression to non-reference type, such as static_cast<double>(x) , std::string{}, or (int)42
- the
this
pointer; - an enumerator;
- a non-type template parameter of a scalar type;
template <int v> void foo() { // not an lvalue, `v` is a template parameter of scalar type int const int* a = &v; // ill-formed v = 3; // ill-formed: lvalue required as left operand of assignment }
|
(since C++11) |
|
(since C++20) |
Properties:
- Same as rvalue (below).
- A prvalue cannot be polymorphic: the dynamic type
- A non-class non-array prvalue cannot be cv-qualified , unless it is materialized in order to be bound to a reference (since C++17)
- A prvalue cannot have incomplete type (except for type void, see below, or when used in
decltype
- A prvalue cannot have abstract class type or an array thereof.
xvalue
The following expressions are xvalue expressions:
-
a.m, the member of object expression, where a is an rvalue and
m
-
a.*mp, the pointer to member of object expression, where a is an rvalue and
mp
- a, b, the built-in comma expression, where b
- a ? b : c, the ternary conditional expression for certain b and c (see definition
|
(since C++11) |
|
(since C++17) |
(since C++23) |
Properties:
- Same as rvalue (below).
- Same as glvalue (below).
In particular, like all rvalues, xvalues bind to rvalue references, and like all glvalues, xvalues may be polymorphic, and non-class xvalues may be cv-qualified
Extended content |
---|
Run this code #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 {}; // Example from C++23 standard: 7.2.1 Value category [basic.lval] struct A { int m; }; A&& operator+(A, A); A&& f(); int main() { A a; A&& ar = static_cast<A&&>(a); // Function call with return type rvalue reference is xvalue static_assert(is_xvalue<decltype( (f()) )>::value); // Member of object expression, object is xvalue, `m` is a non-static data member static_assert(is_xvalue<decltype( (f().m) )>::value); // A cast expression to rvalue reference static_assert(is_xvalue<decltype( (static_cast<A&&>(a)) )>::value); // Operator expression, whose return type is rvalue reference to object static_assert(is_xvalue<decltype( (a + a) )>::value); // Expression `ar` is lvalue, `&ar` is valid static_assert(is_lvalue<decltype( (ar) )>::value); [[maybe_unused]] A* ap = &ar; } |
Mixed categories
glvalue
A glvalue expression is either lvalue or xvalue.
Properties:
- A glvalue may be implicitly converted to a prvalue with lvalue-to-rvalue, array-to-pointer, or function-to-pointer implicit conversion.
- A glvalue may be polymorphic: the dynamic type
- A glvalue can have incomplete type, where permitted by the expression.
rvalue
An rvalue expression is either prvalue or xvalue.
Properties:
- Address of an rvalue cannot be taken by built-in address-of operator: &int(), &i++ [3], &42, and &std::move(x)
- An rvalue can't be used as the left-hand operand of the built-in assignment or compound assignment operators.
- An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the temporary object identified by the rvalue is extended
|
(since C++11) |
Special categories
Pending member function call
The expressions a.mf and p->mf, where mf
is a non-static member function, and the expressions a.*pmf and p->*pmf, where pmf
is a pointer to member function
(p->*pmf)(args)
Void expressions
Function call expressions returning void, cast expressions to void, and throw-expressions return statement in a function returning void. In addition, throw-expressions may be used as the second and the third operands of the conditional operator ?:
Void expressions have no result object. |
(since C++17) |
Bit-fields
An expression that designates a bit-field (e.g. a.m, where a is an lvalue of type struct A { int m: 3 ; }
Move-eligible expressionsAlthough an expression consisting of the name of any variable is an lvalue expression, such expression may be move-eligible if it appears as the operand of If an expression is move-eligible, it is treated either as an rvalue or as an lvalue(until C++23) as an rvalue(since C++23) for the purpose of overload resolution (thus it may select the move constructor). See Automatic move from local variables and parameters |
(since C++11) |
History
CPL
The programming language CPL rvalue). When evaluated in left-hand mode an expression effectively gives an address (the left-hand value, or lvalue
C
The C programming language followed a similar taxonomy, except that the role of assignment was no longer significant: C expressions are categorized between "lvalue expressions" and others (functions and non-object values), where "lvalue" means an expression that identifies an object, a "locator value"[4]
C++98
Pre-2011 C++ followed the C model, but restored the name "rvalue" to non-lvalue expressions, made functions into lvalues, and added the rule that references can bind to lvalues, but only references to const can bind to rvalues. Several non-lvalue C expressions became lvalue expressions in C++.
C++11
With the introduction of move semantics in C++11, value categories were redefined to characterize two independent properties of expressions[5]:
- has identity: it's possible to determine whether the expression refers to the same entity as another expression, such as by comparing addresses of the objects or the functions they identify (obtained directly or indirectly);
- can be moved from: move constructor, move assignment operator
In C++11, expressions that:
- have identity and cannot be moved from are called lvalue expressions;
- have identity and can be moved from are called xvalue expressions;
- do not have identity and can be moved from are called prvalue ("pure rvalue") expressions;
- do not have identity and cannot be moved from are not used[6].
The expressions that have identity are called "glvalue expressions" (glvalue stands for "generalized lvalue"). Both lvalues and xvalues are glvalue expressions.
The expressions that can be moved from are called "rvalue expressions". Both prvalues and xvalues are rvalue expressions.
C++17
In C++17, copy elision
Footnotes
- ↑ Assuming i has built-in type or the pre-increment operator is overloaded
- ↑ 2.0 2.1 2.2 2.3 Special rvalue category, see pending member function call.
- ↑ Assuming i has built-in type or the post-increment operator is not overloaded
- ↑ "A difference of opinion within the C community centered around the meaning of lvalue, one group considering an lvalue to be any kind of object locator, another group holding that an lvalue is meaningful on the left side of an assigning operator. The C89 Committee adopted the definition of lvalue as an object locator." -- ANSI C Rationale, 6.3.2.1/10.
- ↑ "New" Value Terminology by Bjarne Stroustrup, 2010.
-
↑
const prvalues (only allowed for class types) and const xvalues do not bind to
T&&
overloads, but they bind to the const T&& overloads, which are also classified as "move constructor" and "move assignment operator" by the standard, satisfying the definition of "can be moved from" for the purpose of this classification. However, such overloads cannot modify their arguments and are not used in practice; in their absence const prvalues and const xvalues bind to const T&
References
- C++23 standard (ISO/IEC 14882:2024):
- 7.2.1 Value category [basic.lval]
- C++20 standard (ISO/IEC 14882:2020):
- 7.2.1 Value category [basic.lval]
- C++17 standard (ISO/IEC 14882:2017):
- 6.10 Lvalues and rvalues [basic.lval]
- C++14 standard (ISO/IEC 14882:2014):
- 3.10 Lvalues and rvalues [basic.lval]
- C++11 standard (ISO/IEC 14882:2011):
- 3.10 Lvalues and rvalues [basic.lval]
- C++98 standard (ISO/IEC 14882:1998):
- 3.10 Lvalues and rvalues [basic.lval]
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 616 | C++11 | member access and member access through pointer to member of an rvalue resulted in prvalue |
reclassified as xvalue |
CWG 1059 | C++11 | array prvalues could not be cv-qualified | allowed |
CWG 1213 | C++11 | subscripting an array rvalue resulted in lvalue | reclassified as xvalue |
See also
C documentation for value categories
|
External links
1. | C++ value categories and decltype demystified — David Mazières, 2021 | |
2. |
Empirically determine value category of expression — StackOverflow
|