Move Semantics — transferring ownership instead of copying
Before C++11, passing or returning a large object meant copying it: returning std::vector from a function — copy; inserting a string into a container — copy. Each copy allocates memory, walks the bytes, and frees the source. Move semantics fixes this: if the source is a temporary or has explicitly released its resource, the receiver transfers ownership instead of duplicating. For std::vector that means stealing the internal pointer — O(1) instead of O(n).
In C++, move sits across several mechanisms: a new value category (xvalue), a new reference kind (T&&), std::move as a cast, move constructors and assignment, the noexcept contract, copy elision, and forwarding references in templates. Traps: std::move on const, a named T&& being an lvalue, return std::move(v) blocking NRVO, missing noexcept disabling moves in std::vector.
Topic map
- Value categories — lvalue, rvalue, prvalue, xvalue, glvalue — what can be moved and why.
- Rvalue references —
T&&, the named-parameter rule, reference collapsing. - std::move and moved-from state —
std::moveas a cast; valid-but-unspecified; theconsttrap. - Move constructor and Rule of Five —
T(T&&) noexcept, the suppression rule, Rule of Zero/Five. - Perfect forwarding —
std::forward, forwarding references,std::movevsstd::forward. - Copy elision (RVO/NRVO) — when no move is needed; why
return std::move(v)is worse thanreturn v. - Move-only types — unique ownership via
= deleteon copy:std::unique_ptr,std::thread.
Common traps
| Mistake | Consequence |
|---|---|
std::move on a const object | Silently picks copy-ctor; no move, no warning |
Using an object after std::move | Reading the valid-but-unspecified state → unpredictable values |
Move constructor without noexcept | std::vector realloc falls back to copying — move benefit lost |
return std::move(v) for a local | Blocks NRVO: guaranteed move instead of "nothing at all" |
Treating b as rvalue after void f(T&& b) | b is an lvalue expression; needs std::move(b) or std::forward<T>(b) |
std::forward<T> outside a template | Always yields rvalue — just write std::move |
| Declaring only move-ctor, expecting copy to work | Copy is implicitly deleted; the compiler errors at the call site |
| Declaring a user destructor, expecting generated move | Move suppressed; a forced move silently falls back to copy |
Interview relevance
Move semantics is one of the most reliable middle/senior C++ interview topics. Interviewers check whether you understand why each piece works the way it does, not syntax.
The most common wrong answer: "std::move moves the object". It does not. It is a cast. The move constructor moves.
What the interviewer is actually checking:
- Understanding of value categories — lvalue/prvalue/xvalue/glvalue.
- Why a named
T&¶meter in the body is an lvalue expression. std::movevsstd::forward— when and why.- Moved-from state: valid but unspecified.
- The functional role of
noexcepton move (std::vectorrealloc). - RVO/NRVO and why
return std::move(v)is worse thanreturn v. std::moveonconst— silent copy.
Common questions:
- What does
std::movedo? Move or cast? - What state is the source in after
std::move? - Why mark the move constructor
noexcept? - How does
std::movediffer fromstd::forward? - What is a forwarding reference, and how is it different from a plain
T&&? - Explain RVO/NRVO and guaranteed copy elision.
- Why does
std::moveon aconstnot move?