What it's for
- Every hand-off of a heavy resource: a vector returned from a function, a string rebuilt in a loop, a unique_ptr passed into its new owner. Move semantics is how C++ avoids copying what the source is about to abandon.
- The sequel to smart pointers and RAII: those chapters made ownership automatic; this one makes ownership transferable — same block, new owner, no duplicate.
Good to know
The first surprise: std::move doesn't move anything. It's a cast — it marks an object as about-to-die so the compiler is allowed to pick a move overload. The move itself is a constructor or assignment operator, and if no move overload fits — say the source is const, or the type has none — the call quietly falls back to copying. The example type here is std::unique_ptr: moving one transfers the stored pointer and leaves the source empty. That emptiness is unique_ptr's own guarantee, not a law — a moved-from standard object is only promised to be valid-but-unspecified, and your own types make their own contracts. Construction and assignment differ: move construction builds a fresh owner around the resource, while move assignment runs on an owner that may already hold one — its old resource is released first, then the transfer happens, so nothing leaks and nothing is released twice. The source's destructor still runs afterward, on the empty object; a moved-from object must stay destructible. Two traps the simulation can't show: a named rvalue reference is itself an lvalue — inside void f(Block&& b), plain b won't move, you must cast again; and a raw owning pointer with a defaulted move copies the pointer without nulling the source — two owners, one delete each, a double-free (the rule of zero exists for this). Copy elision is a separate mechanism that can skip the move entirely — which is why return std::move(local) can hurt: it can block NRVO and force a move that elision would have avoided. And in containers, relocation prefers moves only when they're noexcept (that's std::move_if_noexcept's whole job); a throwing move constructor makes vector fall back to copying to keep its strong exception guarantee.
Why the steps go in this order
- The copy comes first: two live owners, two blocks — correct, safe, and exactly the cost the chapter is about to remove.
- The cast is named before any transfer: std::move only enables the overload, and seeing it separated from the transfer is what kills the "std::move moves things" myth.
- Move construction before move assignment: construction is the clean case (a new owner, one block, identity preserved); assignment adds the old destination's cleanup, shown as its own beat so no release is ever ambiguous.
- The source's destructor runs last on screen: the moved-from object isn't gone, it's empty — and that distinction is the one reviewers actually check.
Before you rely on it
This is a toy: two owner boxes, one heap block, and arrows for ownership. The code card is real C++ and unique_ptr's behavior — transfer on move, empty source, old destination released first on assignment — is its documented contract, not something the simulation measured. What the toy flattens: moved-from guarantees are type-specific (empty is unique_ptr's promise, not everyone's); moves aren't automatically cheap or O(1); const sources and missing overloads fall back to copying; and containers move only when the type plays along with noexcept. Perfect forwarding and reference collapsing are deliberately left out — they're their own chapter.