LESSON 06 · BEGINNER4 min read

Value Categories, Lvalue References, and Rvalue References

Deconstruct C++ value categories (lvalue, prvalue, xvalue), reference binding rules, lifetime extension of temporaries, and rvalue references (&&).

C++20 / C++23Free to readModule 2 of 11
YOUR NEXT SMALL WINS

By the end, you’ll be able to…

  • Classify expressions into primary value categories: glvalue, lvalue, rvalue, prvalue, and xvalue
  • Compare lvalue references (T&) with const lvalue references (const T&) and lifetime extension rules
  • Synthesize rvalue references (T&&) to enable move semantics and prevent expensive deep copying

01The Taxonomy of C++ Value Categories

In C++, every expression is characterized by two independent properties: Identity (does the program have a pointer or name to locate the object in memory?) and Moveability (can the object's resources be safely stolen or moved?). This gives rise to the complete value category taxonomy formalizing modern C++:

An lvalue has identity and cannot be moved (e.g., named variables like int x = 10;, function calls returning references, array index expressions). You can take the address of an lvalue via &x.

A prvalue ('pure rvalue') has no identity but can be moved (e.g., literal constants like 42, arithmetic temporaries like a + b, function calls returning values).

An xvalue ('eXpiring value') has identity and can be moved. It represents an object whose resources are near the end of their lifetime (e.g., the result of std::move(x)). Together, prvalues and xvalues form rvalues.

example.js⚙️C++
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

02Reference Binding Rules and Const Lifetime Extension

A standard non-const lvalue reference (T&) can ONLY bind to a modifiable lvalue. Attempting to write int& ref = 42; is a compilation error because a temporary literal has no permanent memory identity.

However, a const lvalue reference (const T&) possesses a unique superpower: it can bind to lvalues, const lvalues, and rvalues! When a const lvalue reference binds to a temporary prvalue (e.g., const std::string& s = get_name();), the C++ runtime extends the lifetime of the temporary object to match the scope of the reference itself.

An rvalue reference (T&&), introduced in C++11, can ONLY bind to rvalues (temporaries or xvalues). It signifies that the referenced object is ephemeral and its internal heap allocations or file descriptors can be pilfered without copying.

03Value Category Venn Diagram & Relationship

Understanding the relationships between the five value categories is essential for mastering template meta-programming and move semantics:

Notice that glvalue ('generalized lvalue') encompasses everything with identity, while rvalue encompasses everything that can be moved.

notes.txtPlain text
                Expressions               /           \       glvalue               rvalue      /       \             /      \  lvalue         xvalue         prvalue[Named vars]   [std::move]    [Temporaries/42](Has Identity) (Has Identity) (No Identity)(Not Moveable) (Is Moveable)  (Is Moveable)

04Production Implications of References

In high-performance C++ systems, pass-by-value for large structs or containers incurs massive CPU performance penalties due to heap allocations in copy constructors.

Always pass large read-only parameters by const reference (const T&). Use rvalue references (T&&) in move constructors and move assignment operators to transfer ownership of resources in O(1) time without heap reallocations.

05Production Systems Architecture & Hardware Trade-Offs

From an operating system and hardware architecture perspective, systems software must account for physical execution realities that compiler abstractions intentionally hide. At the hardware level, instruction caches (I-Cache) and data caches (D-Cache) operate independently, and CPU pipeline front-ends rely heavily on branch target buffers (BTB) to minimize instruction fetch stalls. When code is organized into coherent, spatially contiguous blocks within translation units, binary code density increases, improving instruction cache hit rates across high-frequency execution loops.

Furthermore, modern linkers such as LLVM lld and mold perform Link-Time Optimization (LTO) across translation units. When LTO is enabled (-flto), the compiler writes LLVM bitcode into object files rather than final machine code, allowing the linker to perform cross-translation-unit function inlining, devirtualization, and dead-code stripping across the entire binary.

LET IT CLICK

The ideas worth keeping.

  • Expressions are partitioned into lvalues, prvalues, and xvalues based on identity and moveability.
  • Pass large read-only objects by const lvalue reference (const T&) to eliminate copying.
  • Rvalue references (T&&) enable O(1) resource transfer via move semantics.
TURN UNDERSTANDING INTO SOMETHING YOU CAN DO

Make it yours.

Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.

01

Exercise 1: Classify each of the following expressions in

Classify each of the following expressions into its precise value category (lvalue, prvalue, or xvalue): 1. int a = 5; 2. a + 10; 3. std::move(a); 4. ++a; 5. a++;

your_turn_1.cpp⚙️C++
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.cppC++
// 1. 'a' -> lvalue (has identity, addressable)// 2. 'a + 10' -> prvalue (temporary result of arithmetic)// 3. 'std::move(a)' -> xvalue (cast to an expiring rvalue with identity)// 4. '++a' -> lvalue (pre-increment mutates 'a' in place and returns reference to 'a')// 5. 'a++' -> prvalue (post-increment creates and returns a temporary copy of previous value)

Can you take the address of the expression using '&'?

02

Exercise 2: Explain why int ref 100 fails to compile

Explain why int& ref = 100; fails to compile, but const int& ref = 100; compiles successfully.

your_turn_2.cpp⚙️C++
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.cppC++
// 100 is a prvalue literal with no identity or persistent memory address.// Non-const lvalue reference (int&) requires an addressable lvalue so changes can be stored.// const int& is explicitly allowed by the C++ standard to bind to temporaries,// extending the temporary's lifetime to match the lifetime of 'ref'.

Recall the temporary lifetime extension rule for const references.

A SMALL QUESTION. A USEFUL NEXT STEP.

What stayed with you?

Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.

A LITTLE ACTIVE RECALL0 / 3 answered
01Which value category describes an expression that has identity and can be moved (such as the result of std::move(x))?
02What happens when a temporary object binds to a const lvalue reference (const T&)?
03Which reference type can bind to an rvalue temporary but NOT to a non-const named lvalue?
⚡CODING CHALLENGE
🔒Required to Mark Lesson Complete

Exercise 1: Classify each of the following expressions in

Classify each of the following expressions into its precise value category (lvalue, prvalue, or xvalue): 1. int a = 5; 2. a + 10; 3. std::move(a); 4. ++a; 5. a++;

EXPECTED OUTPUT
Build completed. Binary executed.
[Exit code: 0]
main.cppC++
Ctrl + ↵
Execution Console
Click "Run & Verify" (or press Ctrl + Enter) to test your code against the challenge.

Keep exploring.

The official references behind this lesson, when you want a second angle or the full details.

One lesson closer.

Reading is a start. Trying it yourself makes the connection.

Solve the hands-on coding challenge in the knowledge section to verify your code and unlock lesson completion.