Introduction to Move Semantics
Stop copying what you can steal — how modern C++ moves resources instead of duplicating them.
std::vector), copying it duplicates that resource byte for byte. A move instead transfers ownership of the existing resource to a new object and leaves the old one empty. Same result, a fraction of the work. Move semantics is the language feature (added in C++11, refined ever since) that makes this safe and automatic.
- tell an lvalue from an rvalue and explain why the distinction matters,
- describe what a copy costs and how a move avoids it,
- use
std::movecorrectly — and explain that it does not, by itself, move anything, - reason about the state of an object after it has been moved from,
- recognise where the compiler moves for you automatically (returns, inserting into containers).
1. lvalues vs rvalues: does it have a name you can point at?
Every expression in C++ is either an lvalue or an rvalue (there are finer categories, but these two carry the intuition). The names come from the sides of an assignment: historically an lvalue could appear on the left of = and an rvalue only on the right.
- An lvalue is an object with a stable identity — a named location in memory you can take the address of. Variables are lvalues:
x,account.balance,numbers[3]. - An rvalue is a temporary value with no lasting name — the result of an expression that is about to disappear. Literals and the results of most expressions are rvalues:
42,a + b,std::string("hi"), the value returned by a function.
The practical test: if you can take its address with &, it is an lvalue; if you cannot, it is an rvalue.
int x = 10;
int& a = x; // OK: x is an lvalue, it has an address
// int& b = 10; // ERROR: 10 is an rvalue, no address to bind to
int&& c = 10; // OK: an rvalue REFERENCE can bind to the temporary 10
int&& d = x + 5; // OK: (x + 5) is a temporary rvalue
That last piece of syntax — int&&, a double ampersand — is an rvalue reference. It is a reference that only binds to rvalues (temporaries). This is the hook move semantics hangs on: a function can offer one overload for const T& (binds to anything, promises not to change it) and another for T&& (binds only to temporaries the caller no longer needs). When the second one is chosen, the function knows it is safe to gut the argument.