lvalue vs rvalue in C++
Understand lvalue vs rvalue in C++, how value categories power move semantics, and when to use std::move and std::forward for fast, copy-free code.
Expected Interview Answer
An lvalue is an expression that names a persistent object with an identifiable memory location you can take the address of, while an rvalue is a temporary or literal value with no persistent identity that typically appears on the right side of an assignment.
This value-category distinction underpins move semantics. An lvalue reference (T&) binds to named objects, whereas an rvalue reference (T&&) binds to temporaries, letting a move constructor or move assignment steal the temporary's resources instead of deep-copying them. Because a named rvalue reference variable is itself an lvalue, you use std::move to cast an lvalue to an rvalue reference and opt into moving. std::forward preserves the original value category in templates, enabling perfect forwarding. Modern C++ refines these into glvalue, prvalue, and xvalue categories, but the practical lvalue-versus-rvalue split is what drives efficient resource transfer.
- Enables move semantics that avoid expensive deep copies
- Lets containers transfer resources instead of duplicating them
- Powers perfect forwarding via std::forward in templates
- Clarifies why only lvalues can be assigned to
- Improves performance of returning and passing large objects
AI Mentor Explanation
An lvalue is a contracted squad player with a permanent locker and shirt number you can always point to and reuse all season. An rvalue is a one-off net bowler brought in for a single session and gone by evening — nameless in the records. std::move is like handing that departing bowler's spare kit to a squad member instead of buying new: you take resources from something that won't be needed again.
Step-by-Step Explanation
Step 1
Test for an lvalue
Ask whether you can take the address of the expression with &; if yes, it names a persistent object and is an lvalue.
Step 2
Spot rvalues
Literals, arithmetic results, and function return-by-value are temporaries — rvalues with no persistent identity.
Step 3
Bind with the right reference
Use T& to bind lvalues and T&& to bind rvalues; a const T& can bind to both.
Step 4
Opt into moving
Apply std::move to cast a named lvalue to an rvalue reference so move constructors/assignment can steal its resources.
Step 5
Forward in templates
Use std::forward with a forwarding reference (T&&) to preserve the caller's original value category — perfect forwarding.
What Interviewer Expects
- A precise definition based on identity and addressability
- Connection between value categories and move semantics
- Understanding that a named rvalue reference is itself an lvalue
- Correct use of std::move versus std::forward
- Awareness of glvalue/prvalue/xvalue refinements
Common Mistakes
- Thinking std::move actually moves something rather than casting to an rvalue reference
- Believing a named T&& variable is an rvalue
- Using std::move on a value you still need afterward
- Confusing std::move with std::forward in generic code
- Assuming rvalues can never be modified (an xvalue's members can be)
Best Answer (HR Friendly)
“An lvalue is a named thing with a lasting home in memory, like a variable, while an rvalue is a throwaway temporary, like the result of 2 + 3. This distinction lets C++ move resources out of temporaries instead of copying them, which makes programs faster.”
Code Example
#include <utility>
#include <string>
void takesLvalue(std::string& s) {} // binds only to lvalues
void takesRvalue(std::string&& s) {} // binds only to rvalues
void demo() {
std::string name = "Ada"; // 'name' is an lvalue
takesLvalue(name); // OK: name is a named object
takesRvalue("temp"); // OK: string literal builds a temporary (rvalue)
takesRvalue(std::move(name)); // std::move casts the lvalue to an rvalue ref
std::string copy = name; // copy constructor (deep copy)
std::string moved = std::move(name); // move constructor steals resources
}
// Perfect forwarding preserves the caller's value category
template <typename T>
void wrapper(T&& arg) { // forwarding reference
takesRvalue(std::forward<T>(arg)); // stays rvalue if caller passed one
}Follow-up Questions
- What is the difference between std::move and std::forward?
- Why is a named rvalue reference treated as an lvalue?
- Explain glvalue, prvalue, and xvalue value categories.
- How does move semantics improve returning large objects from functions?
- What is a forwarding (universal) reference and when does T&& become one?
MCQ Practice
1. Which of these is an rvalue?
The result of a + b is a temporary with no persistent identity or address, making it an rvalue.
2. What does std::move actually do?
std::move performs an unconditional cast to an rvalue reference, enabling a move constructor or move assignment to run; it does not move anything by itself.
3. Inside a function, a parameter declared as std::string&& s is what kind of expression when you use s?
A named rvalue reference variable has a name and address, so using it is an lvalue expression; you need std::move to treat it as an rvalue again.
Flash Cards
lvalue — An expression naming a persistent object with an address you can take with &.
rvalue — A temporary or literal with no persistent identity, typically on the right of an assignment.
std::move — An unconditional cast to an rvalue reference that enables moving; it does not move by itself.
std::forward — Preserves the original value category in templates for perfect forwarding.
Named T&& is... — An lvalue — because it has a name and address, despite being an rvalue reference type.