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.
decltype
;
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 }
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 contentDespite 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); }[edit] Primary categories [edit] lvalue
The following expressions are lvalue expressions:
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 }
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 }
m
is a member enumerator or a non-static member function, or where a is an rvalue and m
is a non-static data member of object type;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 }
m
is a member enumerator or a non-static member function;mp
is a pointer to data member;mp
is a pointer to data member;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>(); }
Properties:
The following expressions are prvalue expressions:
m
is a member enumerator or a non-static member function[2];m
is a member enumerator or a non-static member function[2];mp
is a pointer to member function[2];mp
is a pointer to member function[2];this
pointer;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 }
Properties:
decltype
specifier).The following expressions are xvalue expressions:
m
is a non-static data member of an object type;mp
is a pointer to data member;Properties:
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 contentRun 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; }[edit] Mixed categories [edit] glvalue
A glvalue expression is either lvalue or xvalue.
Properties:
An rvalue expression is either prvalue or xvalue.
Properties:
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, are classified as prvalue expressions, but they cannot be used to initialize references, as function arguments, or for any purpose at all, except as the left-hand argument of the function call operator, e.g. (p->*pmf)(args).
Function call expressions returning void, cast expressions to void, and throw-expressions are classified as prvalue expressions, but they cannot be used to initialize references or as function arguments. They can be used in discarded-value contexts (e.g. on a line of its own, as the left-hand operand of the comma operator, etc.) and in the 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) [edit] Bit-fieldsAn expression that designates a bit-field (e.g. a.m, where a is an lvalue of type struct A { int m: 3; }) is a glvalue expression: it may be used as the left-hand operand of the assignment operator, but its address cannot be taken and a non-const lvalue reference cannot be bound to it. A const lvalue reference or rvalue reference can be initialized from a bit-field glvalue, but a temporary copy of the bit-field will be made: it won't bind to the bit-field directly.
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 for details.
(since C++11) [edit] History [edit] CPLThe programming language CPL was first to introduce value categories for expressions: all CPL expressions can be evaluated in "right-hand mode", but only certain kinds of expression are meaningful in "left-hand mode". When evaluated in right-hand mode, an expression is regarded as being a rule for the computation of a value (the right-hand value, or rvalue). When evaluated in left-hand mode an expression effectively gives an address (the left-hand value, or lvalue). "Left" and "Right" here stood for "left of assignment" and "right of assignment".
[edit] CThe 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].
[edit] C++98Pre-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++.
[edit] C++11With the introduction of move semantics in C++11, value categories were redefined to characterize two independent properties of expressions[5]:
In C++11, expressions that:
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.
[edit] C++17In C++17, copy elision was made mandatory in some situations, and that required separation of prvalue expressions from the temporary objects initialized by them, resulting in the system we have today. Note that, in contrast with the C++11 scheme, prvalues are no longer moved from.
[edit]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& overloads.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 throughRetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4