Appendix B

Value Categories

Expressions are a cornerstone of the C++ language, providing the primary mechanism by which it can express computations. Every expression has a type, which describes the static type of the value that its computation produces. The expression 7 has type int, as does the expression 5 + 2, and the expression x if x is a variable of type int. Each expression also has a value category, which describes something about how the value was formed and affects how the expression behaves.

B.1 Traditional Lvalues and Rvalues

Historically, there were only two value categories: lvalues and rvalues. Lvalues are expressions that refer to actual values stored in memory or in a machine register, such as the expression x where x is the name of a variable. These expressions may be modifiable, allowing one to update the stored value. For example, if x is a variable of type int, the following assignment will replace the value of x with 7:

x = 7;

The term lvalue is derived from the role these expressions could play within an assignment: The letter “l” stands for “left-hand side” because (historically, in C) only lvalues may occur on the left-hand side of the assignment. Conversely, rvalues (where “r” stands for “right-hand side”) could occur only on the right-hand side of an assignment expression.

However, when C was standardized in 1989, things changed: While an int const still was a value stored in memory, it could not occur on the left-hand side of an assignment:

int const x; // x is a nonmodifiable lvalue
x = 7;      // ERROR: modifiable lvalue required on the left

C++ changed things even further: Class rvalues can occur on the left-hand side of assignments. Such assignments are actually function calls to the appropriate assignment operator of the class rather than “simple” assignments for scalar types, so they follow the (separate) rules of member function calls.

Because of all these changes, the term lvalue is now sometimes said to stand for localizable value. Expressions that refer to a variable are not the only kind of lvalue expression. Another class of expressions that are lvalues include pointer dereference operations (e.g., *p), which refer to the value stored at the address the pointer references, and expressions that refer to a member of a class object (e.g., p->data). Even calls to functions that return values of “traditional” lvalue reference type declared with & are lvalues. For example (see Section B.4 on page 679 for details):

std::vector<int> v;
v.front()           //yields an lvalue because the return type is an lvalue reference

Perhaps surprisingly, string literals are also (nonmodifiable) lvalues.

Rvalues are pure mathematical values (such as 7 or the character ’a’) that don’t necessarily have any associated storage; they come into existence for the purpose of a computation but cannot be referenced again once they have been used. In particular, any literal value except string literals (e.g., 7, ’a’, true, nullptr) are rvalues, as are the results of many built-in arithmetic computations (e.g., x + 5 for x of integer type) and calls to functions that return a result by value. That is, all temporaries are rvalues. (That doesn’t apply to named references that refer to them, though.)

B.1.1 Lvalue-to-Rvalue Conversions

Due to their ephemeral nature, rvalues are necessarily restricted to the right-hand side of a (“simple”) assignment: An assignment 7 = 8 doesn’t make sense because the mathematical 7 isn’t allowed to be redefined. Lvalues, on the other hand, don’t appear to have the same restriction: One can certainly compute the assignment x = y when x and y are variables of compatible type, even though the expressions x and y are both lvalues.

The assignment x = y works because the expression on the right-hand side, y, undergoes an implicit conversion called the lvalue-to-rvalue conversion. As its name implies, the lvalue-to-rvalue conversion takes an lvalue and produces an rvalue of the same type by reading from the storage or register associated with the lvalue. This conversion therefore accomplishes two things: First, it ensures that an lvalue can be used wherever an rvalue is expected (e.g., as the right-hand side of an assignment or in a mathematical expression such as x + y). Second, it identifies where in the program the compiler (prior to optimization) may emit a “load” instruction to read a value from memory.

B.2 Value Categories Since C++11

When rvalue references were introduced in C++11 in support of move semantics, the traditional partitioning of expressions into lvalues and rvalues was no longer sufficient to describe all the C++11 language behaviors. The C++ standardization committee therefore redesigned the value category system based on three core and two composite categories (see Figure B.1). The core categories are: lvalue, prvalue (“pure rvalue”), and xvalue. The composite categories are: glvalue (“generalized lvalue,” which is the union of lvalue and xvalue) and rvalue (the union of xvalue and prvalue).

Note that all expressions are still either lvalues or rvalues, but the rvalues category is now further subdivided.

Figure B.1. Value Categories since C++11

This C++11 categorization has remained in effect, but in C++17 the characterization of the categories were reformulated as follows:

• A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function (i.e., an entity that has storage).

• A prvalue is an expression whose evaluation initializes an object or a bit-field, or computes the value of the operand of an operator.

• An xvalue is a glvalue designating an object or bit-field whose resources can be reused (usually because it is about to “expire”—the “x” in xvalue originally came from “eXpiring value”).

• An lvalue is a glvalue that is not an xvalue.

• An rvalue is an expression that is either a prvalue or an xvalue.

Note that in C++17 (and to some extent, in C++11 and C++14), the glvalue vs. prvalue dichotomy is arguably more fundamental than the traditional lvalue vs. rvalue distinction.

Although this describes the characterization introduced in C++17, those descriptions also apply to C++11 and C++14 (the prior descriptions were equivalent but harder to reason about).

Except for bit fields, glvalues produce entities with an address. That address may be that of a subobject of a larger enclosing object. In the case of a base class subobject, the type of the glvalue (expression) is called its static type, and the type of the most derived object that base class is part of is called the dynamic type of the glvalue. If the glvalue does not produce a base class subobject, its static and dynamic types are identical (i.e., the type of the expression).

Examples of lvalues are:

• Expressions that designate variables or functions

• Applications of the built-in unary * operator (“pointer indirection”)

• An expression that is just a string literal

• A call to a function with a return type that is an lvalue reference

Examples of prvalues are:

• Expressions that consist of a literal that is not a string literal or a user-defined literal1

• Applications of the built-in unary & operator (i.e., taking the address of an expression)

• Applications of built-in arithmetic operators

• A call to a function with a return type that is not a reference type

• Lambda expressions

Examples of xvalues are:

• A call to a function with a return type that is an rvalue reference to an object type (e.g., std::move())

• A cast to an rvalue reference to an object type

Note that rvalue references to function types produce lvalues, not xvalues.

It’s worth emphasizing that glvalues, prvalues, xvalues, and so on, are expressions, and not values2 or entities. For example, a variable is not an lvalue even though an expression denoting a variable is an lvalue:

int x = 3;  // x here is a variable, not an lvalue. 3 is a prvalue initializing
            // the variable x.
int y = x;  // x here is an lvalue. The evaluation of that lvalue expression does not
            // produce the value 3, but a designation of an object containing the value 3.
            // That lvalue is then then converted to a prvalue, which is what initializes
y.

B.2.1 Temporary Materialization

We previously mentioned that lvalues often undergo an lvalue-to-rvalue conversion3 because prvalues are the kinds of expressions that initialize objects (or provide the operands for most built-in operators).

In C++17, there is a dual to this conversion, known as temporary materialization (but it could just as well have been called “prvalue-to-xvalue conversion”): Any time a prvalue validly appears where a glvalue (which includes the xvalue case) is expected, a temporary object is created and initialized with the prvalue (recall that prvalues are primarily “initializing values”), and the prvalue is replaced by an xvalue designating the temporary. For example:

    int f(int const&);
    int r = f(3);

Because f() in this example has a reference parameter, it expects a glvalue argument. However, the expression 3 is a prvalue. The “temporary materialization” rule therefore kicks in, and the expression 3 is “converted” to an xvalue designating a temporary object initialized with the value 3.

More generally, a temporary is materialized to be initialized with a prvalue in the following situations:

• A prvalue is bound to a reference (e.g., that call f(3) above).

• A member of a class prvalue is accessed.

• An array prvalue is subscripted.

• An array prvalue is converted to a pointer to its first element (i.e., array decay).

• A prvalue appears in a braced initializer list that, for some type X, initializes an object of type std::initializer_list<X>.

• The sizeof or typeid operator is applied to a prvalue.

• A prvalue is the top-level expression in a statement of the form “expr;” or an expression is cast to void.

Thus, in C++17, the object initialized by a prvalue is always determined by the context, and, as a result, temporaries are created only when they are really needed. Prior to C++17, prvalues (particularly of class type) always implied a temporary. Copies of those temporaries could optionally be elided later on, but a compiler still had to enforce most semantics constraints of the copy operation (e.g., a copy constructor may need to be callable). The following example shows a consequence of the C++17 revision of the rules:

class N {
 public:
  N();
  N(N const&) = delete;  // this class is neither copyable …
  N(N&&) = delete;       // … nor movable
};

N make_N() {
  return N{};      // Always creates a conceptual temporary prior to C++17.
}                  // In C++17, no temporary is created at this point.
auto n = make_N(); // ERROR prior to C++17 because the prvalue needs a
                   // conceptual copy. OK since C++17, because n is
                   // initialized directly from the prvalue.

Prior to C++17, the prvalue N{} produced a temporary of type N, but compilers were allowed to elide copies and moves of that temporary (which they always did, in practice). In this case, that means that the temporary result of calling make_N() can be constructed directly in the storage of n; no copy or move operation is needed. Unfortunately, pre-C++17 compilers still have to check that a copy or move operation could be made, and in this example that is not possible because the copy constructor of N is deleted (and no move constructor is generated). Hence, C++11 and C++14 compilers must issue an error for this example.

With C++17 the prvalue N itself does not produce a temporary. Instead, it initializes an object determined by the context: In our example, that object is the one denoted by n. No copy or move operation is ever considered (this is not an optimization, but a language guarantee) and therefore the code is valid C++17.

We conclude with an example that shows a variety of value category situations:

class X {
};
X v;
X const c;

void f(X const&);   // accepts an expression of any value category
void f(X&&);        // accepts prvalues and xvalues only but is a better match
                    // for those than the previous declaration
f(v);               // passes a modifiable lvalue to the first
f()f(c);            // passes a nonmodifiable lvalue to the first f()
f(X());             // passes a prvalue (since C++17 materialized as xvalue) to the 2nd f()
f(std::move(v));    // passes an xvalue to the second f()

B.3 Checking Value Categories with decltype

With the keyword decltype (introduced in C++11), it is possible to check the value category of any C++ expression. For any expression x, decltype((x)) (note the double parentheses) yields:

type if x is a prvalue

type& if x is an lvalue

type&& if x is an xvalue

The double parentheses in decltype((x)) are needed to avoid producing the declared type of a named entity in case where the expression x does indeed name an entity (in other cases, the parentheses have no effect). For example, if the expression x simply names a variable v, the construct without parentheses becomes decltype(v), which produces the type of the variable v rather than a type reflecting the value category of the expression x referring to that variable.

Thus, using type traits for any expression e, we can check its value category as follows:

if constexpr (std::is_lvalue_reference<decltype((e))>::value) {
  std::cout << "expression is lvalue\n";
}
else if constexpr (std::is_rvalue_reference<decltype((e))>::value) {
  std::cout << "expression is xvalue\n";
}
else {
  std::cout << "expression is prvalue\n";
}

See Section 15.10.2 on page 298 for details.

B.4 Reference Types

Reference types in C++—such as int&—interact with value categories in two important ways. The first is that a reference may limit the value category of an expression it can bind to. For example, a non-const lvalue reference of type int& can only be initialized with an expression that is an lvalue of type int. Similarly, an rvalue reference of type int&& can only be initialized with an expression that is an rvalue of type int.

The second way in which value categories interact with references is with the return types of functions, where the use of a reference type as the return type affects the value category of a call to that function. In particular:

• A call to a function whose return type is an lvalue reference yields an lvalue.

• A call to a function whose return type is an rvalue reference to an object type yields an xvalue (rvalue references to function types always result in lvalues).

• A call to a function that returns a nonreference type yields a prvalue.

We illustrate the interactions between reference types and value categories in the following example. Given:

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

both the value category and type of a given expression can be determined via decltype. As described in Section 15.10.2 on page 298, it uses reference types to describe when the expression is an lvalue or xvalue:

std::is_same_v<decltype(lvalue()), int&     // yields true because result is lvalue
std::is_same_v<decltype(xvalue()), int&&>   // yields true because result is xvalue
std::is_same_v<decltype(prvalue()), int>    // yields true because result is prvalue

Thus, the following calls are possible:

int& lref1 = lvalue();        // OK: lvalue reference can bind to an lvalue
int& lref3 = prvalue();       // ERROR: lvalue reference cannot bind to a prvalue
int& lref2 = xvalue();        // ERROR: lvalue reference cannot bind to an xvalue

int&& rref1 = lvalue();       // ERROR: rvalue reference cannot bind to an lvalue
int&& rref2 = prvalue();      // OK: rvalue reference can bind to a prvalue
int&& rref3 = xvalue();       // OK: rvalue reference can bind to an xrvalue

1 User-defined literals can lead to lvalues or rvalues, depending on the return type of the associated literal operator.

2 Which unfortunately means that these terms are misnomers.

3 In the world of C++11 value categories, the phrase glvalue-to-prvalue conversion would be more accurate, but the traditional term remains more common.