Move Semantics & Rvalue References
Move semantics ও rvalue reference
1. The Problem Move Solves
Returning a 1GB std::vector from a function shouldn't require copying 1GB. Before C++11,
it sometimes did. Move semantics let the new owner steal the underlying buffer in O(1) instead.
2. lvalue vs rvalue
| Category | Example | Has identity? |
|---|---|---|
| lvalue | x, arr[i], *p | Yes (named) |
| rvalue | 42, x + 1, foo() | No (temporary) |
T&binds to lvalues onlyconst T&binds to anythingT&&binds to rvalues — "you may steal from me"
3. Move Constructor & Assignment
#include <iostream>
#include <vector>
#include <string>
class Buffer {
std::string data_;
public:
Buffer(std::string s) : data_(std::move(s)) {}
Buffer(const Buffer& o) : data_(o.data_) {
std::cout << "copy\n";
}
Buffer(Buffer&& o) noexcept : data_(std::move(o.data_)) {
std::cout << "move\n";
}
};
int main() {
Buffer a{"hello"};
Buffer b = a; // copy
Buffer c = std::move(a); // move
}
4. std::move — Just a Cast
std::move(x) doesn't actually move anything. It casts x to T&&,
telling the compiler "treat this as an rvalue — feel free to steal." Whoever takes the move ref does the actual work.
5. Why noexcept on Move?
STL containers (vector reallocation, etc.) only use move if the move is noexcept.
Otherwise they copy for strong exception safety. Always mark your move ctor / move assign as noexcept.
6. Perfect Forwarding (Preview)
In templates, T&& is a "forwarding reference" — keeps lvalue/rvalue-ness:
template<typename T>
void wrapper(T&& arg) {
target(std::forward<T>(arg)); // preserves value category
}
7. Practice Problems
- Lvalue or rvalue?
x + 1✨ Show Answer
rvalue (temporary).
- Lvalue or rvalue?
arr[5]✨ Show Answer
lvalue.
- What does
std::move(x)actually do?✨ Show Answer
Casts x to T&&. Just a static_cast. No data is moved.
- Why must move ops be noexcept for vector to use them?
✨ Show Answer
Vector's reallocation provides strong exception safety. If a move can throw, vector must use copy instead so a partial reallocation doesn't lose elements.
- Write a non-throwing move constructor.
✨ Show Answer
X(X&& o) noexcept : data_(std::move(o.data_)) {} - After
auto y = std::move(x), what's safe to do with x?✨ Show Answer
Assign a new value, destroy it. Don't read meaningful data — it's in an unspecified state.
- Why is
const T&&rare?✨ Show Answer
You can't move from a const object — there's nothing to steal. So const rvalue refs are mostly useless.
- Pass a temp string to a function: copy or move?
✨ Show Answer
If the parameter is
std::string s(by value), the temp is moved intosfor free. - Why use
data_(std::move(s))in a constructor body?✨ Show Answer
The parameter
sis an lvalue inside the function. To enable move construction ofdata_, cast s to rvalue withstd::move. - Will the compiler auto-generate move ops?
✨ Show Answer
Only if you haven't declared destructor, copy ctor, copy assign, or move assign. Otherwise, the move ops are suppressed and you must write or default them.
- Force defaulted move ctor.
✨ Show Answer
X(X&&) noexcept = default; - Why do unique_ptr move but not copy?
✨ Show Answer
Sole-ownership invariant: only one owner. Copying would mean two owners → double-delete. Move transfers ownership.
- Show that returning a local string is moved.
✨ Show Answer
std::string make() { std::string s = "big string"; return s; } // no copy: NRVO or implicit move - What is RVO / NRVO?
✨ Show Answer
Return-Value Optimization. Compiler constructs the return value directly in the caller's space — no move, no copy. C++17 makes RVO mandatory in many cases.
- When NOT to mark move noexcept?
✨ Show Answer
If your move can actually throw. But ideally design so it can't (move pointers/handles, leave the source empty).
- Is
std::move(x)the same as moving x?✨ Show Answer
No. It just enables moving. The actual move happens when something binds the rvalue ref and steals from it.
Summary
Move semantics let big objects transfer ownership in O(1) instead of copying. T&& is an
rvalue reference. std::move is a cast that enables moves; the actual work happens in the move
ctor / assign. Always mark moves noexcept. After move, source is in an unspecified-but-valid state.