Pointers and Memory Management
Move Semantics
Rvalue references and std::move — transferring resources without copying
Interview: Core C++11 feature — the most commonly asked advanced topic: rvalue vs lvalue, move constructor, Rule of Five
Move Semantics
Move semantics (C++11) enable transferring resources from one object to another without copying. Before C++11, returning a vector from a function required copying all its elements — O(n) cost. With move semantics, the internal buffer is simply "stolen" — the pointer is transferred and the source is left empty — O(1) cost.
Lvalues and Rvalues
An lvalue is an expression with a persistent identity — it has a memory address and can appear on the left side of assignment (variables, array elements, dereferenced pointers). An rvalue is a temporary value without persistent identity — it cannot be assigned to (literals, function return values, expressions like a + b). The key insight: rvalues are about to be destroyed, so stealing their resources is safe.
Rvalue References (T&&)
An rvalue reference (T&&) binds to rvalues — temporaries. It enables writing move constructors and move assignment operators that accept temporaries and steal their resources. std::move(x) is an unconditional cast of x to an rvalue reference — it does not move anything itself; it just signals that the object can be moved from.
The Rule of Five
If a class manages a resource and defines any of the five special members (destructor, copy constructor, copy assignment, move constructor, move assignment), it should define all five. The compiler does not generate move operations if you define a destructor or copy operations — you must define them explicitly.
Interview Corner
Q: What is the state of an object after it has been moved from?
A: The C++ standard says a moved-from object is in a valid but unspecified state. You can call its destructor and reassign it, but you cannot rely on its value. For standard library types: a moved-from std::vector is empty, a moved-from std::string is empty, a moved-from unique_ptr is nullptr. Convention for user-defined types: after a move constructor/assignment, leave the source in a default-constructed-like state.
Q: Does std::move actually move anything?
A: No. std::move is just an unconditional cast to an rvalue reference — it changes the expression's value category from lvalue to rvalue. The actual resource transfer happens in the move constructor or move assignment operator that receives the rvalue reference. If a class has no move constructor, std::move falls back to the copy constructor silently.
Q: What is Return Value Optimization (RVO) and how does it relate to moves?
A: RVO (and NRVO) allows the compiler to construct the return value directly in the caller's memory, eliminating the copy entirely — better than a move. C++17 mandates copy elision for prvalue returns (RVO). NRVO (Named RVO) is optional but most compilers apply it. Implication: returning a local variable by value is efficient — don't add std::move to a return statement as it can prevent elision.
Common Pitfalls
- Moving from a named variable in a return:
return std::move(localVar);prevents NRVO — the compiler cannot elide the move. Just return the variable directly. - Using a moved-from object: Accessing a moved-from object's value (not just assigning to it) is undefined behavior or at least implementation-defined.
- Move constructor not noexcept: If a move constructor can throw, containers like
std::vectorfall back to copying during reallocation (for strong exception safety). Always mark move constructorsnoexcept.
Best Practices
- Always mark move constructors and move assignment operators
noexcept— enables move optimization in standard containers. - Follow the Rule of Zero — use RAII members so the compiler generates correct move operations automatically.
- Don't use
std::moveon return values from functions — let the compiler apply RVO/NRVO.