5.2.9C++ Programming

Move semantics — rvalue references (&&), std - move, std - forward

2,331 words11 min readdifficulty · medium6 backlinks

WHY does this exist?


WHAT is an rvalue reference?

Figure — Move semantics — rvalue references (&&), std - move, std - forward

HOW does a move constructor work? (Derive from scratch)

Imagine a tiny string class owning a heap buffer.

class Str {
    char* data;      // owns heap memory
    size_t len;
public:
    // Copy constructor: DEEP copy (expensive)
    Str(const Str& o) : len(o.len) {
        data = new char[len+1];          // allocate
        std::memcpy(data, o.data, len+1);// copy all bytes
    }                                    // o is left untouched
 
    // Move constructor: STEAL (cheap)
    Str(Str&& o) noexcept
        : data(o.data), len(o.len) {     // grab his pointer
        o.data = nullptr;                // leave source EMPTY but valid
        o.len  = 0;
    }
    ~Str() { delete[] data; }            // safe: nullptr delete is no-op
};

WHAT is std::move?


WHY do we need std::forward? (Perfect forwarding)


std::move vs std::forward


Common Mistakes (Steel-manned)


Worked example: a class with copy + move


Recall Feynman: explain to a 12-year-old

Imagine you're moving house. Copying is buying all-new furniture identical to your old stuff — slow and wasteful. Moving is just driving your existing furniture to the new house. && is a sign on the truck saying "this furniture is up for grabs, the old house is being demolished." std::move is you putting that sign on your own furniture to say "I'm done with this, take it." std::forward is a delivery guy who carefully keeps the sign exactly as it was, so the next person knows whether to copy or take the real thing.


Flashcards

What does std::move(x) actually do at runtime?
Nothing — it is a compile-time static_cast<T&&>(x). It only enables selecting a move constructor/assignment.
What binds to a T&& (non-template) parameter?
Only rvalues (temporaries); not lvalues.
Why must a moved-from object be reset (e.g. pointer set to null)?
To avoid double-free / double-delete; it must be left valid-but-unspecified so its destructor is safe.
Why mark a move constructor noexcept?
So std::vector (and others) will use it during reallocation; otherwise they fall back to copying for exception safety.
Difference between std::move and std::forward?
move is an unconditional cast to rvalue; forward<T> is a conditional cast preserving the caller's lvalue/rvalue category, used only with forwarding references.
What is a forwarding (universal) reference?
A T&& where T is a deduced template parameter; it binds to both lvalues and rvalues via reference collapsing.
Reference collapsing rules?
T& &, T& &&, T&& & all → T&; only T&& &&T&&.
Why is return std::move(localVar); usually a bad idea?
It disables copy elision/NRVO; plain return localVar; already moves or elides and is at least as fast.
Inside void f(Str&& s), is s an lvalue or rvalue?
An lvalue (it has a name). You must std::move(s) to move from it.
If a type has no move constructor, what does std::move(x) select?
The copy constructor — silently, with no error.

Connections

  • Rule of Five — destructor, copy ctor, copy assign, move ctor, move assign
  • RAII and Ownership — moves transfer ownership of resources
  • Copy elision and RVO — when the compiler skips construction entirely
  • Templates and type deduction — basis for forwarding references
  • std::vector internals — uses moves during reallocation
  • Reference collapsing — the rule that makes forwarding work
  • noexcept specifier

Concept Map

motivates

transfer ownership

contrast

binds to

enables

grabs pointer then

prevents

casts lvalue to

overload vs const T&

lets vector use move

preserves value category

Deep copy of temporaries wastes work

Move semantics

Steal heap buffer

lvalue: named, has address

rvalue: temporary, expiring

rvalue reference T&&

Move constructor

Reset source to nullptr

double delete UB

std - move

Compiler picks move or copy

noexcept

std - forward

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, move semantics ka core idea bahut simple hai. Maan lo aapke paas ek bada std::vector ya std::string hai jiske andar heap pe ek lambi memory buffer hai. Agar aap usko kisi aur ko dena chahte ho, do tareeke hain: copy (puri nayi memory allocate karke har byte copy karo — slow aur waste) ya move (sirf pointer utha ke de do, original ko khaali kar do — super fast). Jab source ek temporary hai jo waise bhi marne wala hai, to copy karna bilkul bekaar hai — bas uska saamaan utha lo.

&& ka matlab hai "rvalue reference" — ye sirf temporaries (jaise a + b, ya function ka return) ko bind karta hai. Ye ek signboard hai jo bolta hai "is object se chori kar sakte ho". std::move koi kaam nahi karta — wo bas ek cast hai jo aapke named variable ko rvalue bana deta hai, taaki compiler move constructor choose kare. Yaad rakho: std::move se speed nahi aati, speed move constructor ke existence se aati hai. Aur ek baar object move ho gaya, to uski value mat padho — wo "valid but unspecified" state mein hai, sirf destroy ya reassign karo.

std::forward thoda advanced hai — ye templates ke andar kaam aata hai. Jab aap template<class T> void f(T&& x) likhte ho, to x ek "forwarding reference" hai jo lvalue aur rvalue dono ko bind kar leta hai. Problem ye hai ki function ke andar x ka ek naam hai, isliye wo automatically lvalue ban jaata hai — rvalue-ness kho jaati hai. std::forward<T>(x) us original category ko wapas restore karta hai, taaki aage callee ko exactly waisa hi mile jaisa caller ne diya tha — perfect forwarding.

Ek important galti se bacho: return std::move(localVar); mat likho. Compiler waise hi copy elision / NRVO karta hai, aur std::move lagane se wo optimization band ho jaati hai. Aur move constructor ko hamesha noexcept mark karo, warna std::vector reallocation ke time move ki jagah copy use karega. Bas itna yaad rakho: move = chori, forward = caller ki marzi preserve karo.

Go deeper — visual, from zero

Test yourself — C++ Programming

Connections