Skip to content

Move Semantics in Depth

Beginning of the lesson

Move Semantics in Depth

Stealing resources instead of copying them - and doing it safely

Big Idea: A copy duplicates the resources an object owns; a move instead transfers ownership of those resources from a source to a destination, leaving the source in a valid but empty state. When the source is a temporary or is explicitly cast with std::move, the compiler can pick the cheap move path over the expensive copy path. Used well, move semantics turn O(n) deep copies into O(1) pointer swaps - with no change to how calling code reads.
By the end you will be able to:
  • read value categories and know exactly when an rvalue reference T&& binds;
  • write a correct move constructor and move assignment operator;
  • explain why marking them noexcept changes program performance, not just its promises;
  • apply the rule of five (and prefer the rule of zero);
  • reason about the moved-from state without triggering undefined behavior.

1. Value categories and rvalue references

Every expression in C++ has both a type and a value category. For everyday move semantics the practical split is between lvalues - things with a name and a stable address you could take with & - and rvalues - temporaries and other objects whose resources are safe to cannibalize because no one will observe them again.

An rvalue reference, written T&&, is a reference that binds only to rvalues. That is the whole trick: an overload taking T&& is selected precisely when the argument is an expiring value, so inside that overload you know it is safe to steal.

void f(const std::string&  s);  // lvalue overload: binds to anything, may not steal
void f(std::string&&        s);  // rvalue overload: binds to temporaries, may steal

std::string name = "Ada";
f(name);              // calls f(const std::string&)  - name is an lvalue
f("Ada");             // calls f(std::string&&)        - the temporary is an rvalue
f(std::move(name));   // calls f(std::string&&)        - we PROMISE not to need name

std::move does not move anything. It is a cast: it produces an rvalue reference to its argument so that a move overload is chosen. The actual work happens inside the move constructor or move assignment that gets selected. Think of std::move as saying "treat this as expiring" - the moving is done by the type, not by the cast.

A named rvalue reference is itself an lvalue. Inside void g(std::string&& s), the parameter s has a name, so to move out of it you must write std::move(s) again. Forgetting this silently makes a copy.

Continue this lesson on YourAIZone

The full lesson has the complete explanation, worked examples, common mistakes, tests with instant checking and an AI tutor. Start right away as a guest, or create an account to keep your progress.

In the full lesson

  • The complete explanation
  • Worked examples
  • Common mistakes and how to avoid them
  • Test questions: 15
  • AI tutor that answers questions about the topic
  • Progress tracking for students, parents and teachers