5.2.9 · Coding › C++ Programming
Intuition Ek-sentence idea
Kabhi kabhi aap kisi object ko kisi aur ko de rahe hote ho aur aapko apni copy ki zaroorat nahi rehti . Move semantics receiver ko allow karta hai ki woh expensive deep copy karne ki jagah andar ki cheez (heap pointer) churaa le . && woh type hai jo kehta hai "yeh ek temporary hai jise tum chura sakte ho", std::move ek cast hai jo kehta hai "isse stealable maano", aur std::forward preserve karta hai ki original stealable tha ya nahi.
Intuition Woh problem jo move semantics solve karta hai
C++11 se pehle, bade objects ko return karna ya pass karna (ek std::vector with a million elements, ya ek std::string) matlab tha deep copies : nayi memory allocate karo, har byte copy karo, purani free karo.
Lekin aksar source ek temporary hota hai joaise bhi khatam hone wala hota hai (ek function ka return value, a + b ka result). Usse copy karna poora waste hai — hum sirf already-allocated buffer ki ownership transfer kar sakte the.
Move semantics = "jo destroy hone wala hai use copy mat karo; relocate karo."
Definition lvalue vs rvalue
Ek lvalue ek aisa expression hai jiska naam / identity / address hota hai jo aap & se le sakte ho (jaise x, arr[i]). Yeh usually persist karta hai.
Ek rvalue ek temporary hota hai jiska koi persistent identity nahi hota (jaise 42, a + b, make_string()). Yeh expire hone wala hai.
Ek ==T&&== (rvalue reference) rvalues se bind hota hai — woh temporaries jinhe aap plunder kar sakte ho.
Definition Reference-binding ke rules
Reference type
Lvalue se bind hota hai?
Rvalue se bind hota hai?
T&
✅
❌
const T&
✅
✅
T&&
❌
✅
Toh const T& (copy) vs T&& (move) par overloading compiler ko choose karne deta hai: named cheez → copy, temporary → move.
Imagine karo ek chhoti si string class jo ek heap buffer own karti hai.
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
};
Intuition Source ko empty reset karna kyun zaroori hai
Jab hum o.data chura lete hain, do objects ek hi buffer ko point karte hain. Jab dono destructors chalte hain, buffer delete[] do baar hota hai → undefined behaviour. o.data = nullptr set karne se, moved-from object ab kuch own nahi karta, toh uska destructor harmless hai. Rule: ek moved-from object ko valid lekin unspecified state mein chhoda jaana chahiye.
noexcept kyun matter karta hai
std::vector reallocation ke waqt aapka move constructor tabhi use karta hai jab woh noexcept ho . Agar moving throw kar sakta hai, toh vector reallocation ke beech strong exception safety guarantee nahi de sakta, isliye woh copying par fall back karta hai. noexcept bhool gaye → silently move performance kho gaye.
std::move sirf ek cast hai
std::move(x) kuch move nahi karta . Yeh ek compile-time cast hai rvalue reference mein:
std::move(x) ≡ static_cast<T&&>(x)
Yeh move ko enable karta hai x ko T&& overload se bind karake. Asli stealing move constructor/assignment ke andar hoti hai jo select hoti hai.
std::move ka use
Str a = makeStr ();
Str b = a; // a is an lvalue → COPY constructor
Str c = std :: move (a); // cast to rvalue → MOVE constructor
// a is now moved-from: don't read its value, only reassign/destroy
Yeh step kyun? a ka ek naam hai, toh by default yeh ek lvalue hai aur copy karta hai. std::move(a) promise karta hai ki aapko a ki aur zaroorat nahi, toh compiler move overload pick karta hai.
Definition Forwarding (universal) reference
Ek template mein, T&& special hai. Jab T deduce hota hai, T&& ek forwarding reference ban jaata hai jo dono lvalues aur rvalues se bind kar sakta hai. Reference collapsing result decide karta hai:
T & && → T & , T && && → T &&
template < class T >
void wrapper ( T && arg ) {
callee (arg); // BUG: arg is a NAMED variable → always an lvalue!
}
Chahe wrapper ko ek temporary ke saath call kiya gaya ho, function ke andar arg ka ek naam hai, toh callee(arg) hamesha copy karta hai. Hum ne rvalue-ness kho di. std::forward isse restore karta hai.
Worked example Correct perfect forwarding
template < class T >
void wrapper ( T && arg ) {
callee ( std :: forward < T >(arg)); // preserves lvalue/rvalue-ness
}
wrapper (x); // T=U& → forwards x as lvalue → copy
wrapper ( makeStr ()); // T=U → forwards as rvalue → move
Yeh step kyun? std::forward<T> deduced T ko dekh kar yaad karta hai ki caller ne argument kaise pass kiya tha, phir usi category mein re-cast karta hai — toh callee wahi dekhta hai jo caller ne intend kiya tha.
std::move khud hi mera code faster banata hai"
Kyun sahi lagta hai: naam move sunke lagta hai kuch kaam karta hai. Reality: yeh ek static_cast hai, zero runtime cost. Speed aati hai move constructor ke exist karne aur select hone se. Aisi type par std::move jisme koi move constructor nahi hai → silently copy constructor call hota hai.
Common mistake "Return value par
std::move use karo: return std::move(x);"
Kyun sahi lagta hai: aap return par copy avoid karna chahte ho. Reality: ek local variable ke liye, return x; already NRVO / implicit move karta hai. return std::move(x); likhne se copy elision disable ho jaata hai aur yeh slower ho sakta hai. Fix: bas return x;.
Common mistake "Moved-from object ko read karna theek hai"
Kyun sahi lagta hai: object abhi bhi exist karta hai. Reality: woh valid lekin unspecified state mein hai — uski value garbage hai. Sirf safe operations: use destroy karo ya nayi value assign karo. Purana data expect karke cout << movedFrom; mat karo.
Common mistake "Safe rehne ke liye
std::forward har jagah use karo"
Kyun sahi lagta hai: forwarding general lagta hai. Reality: std::forward<T> tab hi sense karta hai jab T ek forwarding reference par deduced template parameter ho. Isse ek concrete T&& parameter par ya non-template par use karna value-category logic tod deta hai. Wahan std::move use karo.
Str&& parameter matlab function ke andar rvalue hai"
Kyun sahi lagta hai: type && hai. Reality: function ke andar parameter ka ek naam hota hai, toh woh ek lvalue hai! Usse actually move karne ke liye aapko phir se std::move karna hoga.
Worked example Full Rule-of-Five sketch
class Buf {
int* p; size_t n;
public:
Buf ( size_t n ): p ( new int [n]), n (n) {}
Buf ( const Buf & o ): p ( new int [o.n]), n (o.n) { // copy
std :: copy (o.p, o.p + n, p);
}
Buf ( Buf && o ) noexcept : p (o.p), n (o.n) { // move
o.p = nullptr ; o.n = 0 ;
}
Buf & operator = ( Buf o ) { // copy-and-swap
std :: swap (p, o.p); std :: swap (n, o.n); return * this ;
}
~Buf () { delete[] p; }
};
operator= ke liye copy-and-swap kyun? Parameter ko by value lene se compiler ko choose karne deta hai ki o banane ke liye copy kare ya move kare. Phir hum apna andar ka saman o ke saath swap karte hain, aur o ka destructor hamara purana data clean up karta hai. Ek assignment copy aur move assignment dono handle karta hai, aur self-assignment safe bhi hai.
Recall Feynman: 12-saal ke bacche ko explain karo
Imagine karo tum ghar shift kar rahe ho. Copying matlab hai apna sara purana furniture bilkul ek jaisa naaya kharidna — slow aur wasteful. Moving matlab hai apna existing furniture seedha naaye ghar le jaana. && truck par ek sign hai jo kehta hai "yeh furniture lene ke liye available hai, purana ghar girayaa ja raha hai." std::move matlab hai tum khud apne furniture par woh sign lagao yeh kehne ke liye "main isse done ho gaya, le lo." std::forward ek delivery wala hai jo sign ko bilkul waisa hi rakhta hai jaise tha, taaki agla banda jaane ki copy karna hai ya asli cheez leni hai.
Mnemonic Teeno yaad rakho
"MOVE hamesha cast karta hai, FORWARD kabhi kabhi cast karta hai, && tumhe MARNE WALE se LOOTNE deta hai."
&& → temporary jise tum ROB kar sako
move → hamesha rvalue
forward → kabhi kabhi rvalue (caller ki category rakhta hai)
std::move(x) runtime par actually kya karta hai?Kuch nahi — yeh ek compile-time static_cast<T&&>(x) hai. Yeh sirf move constructor/assignment select karne ko enable karta hai.
T&& (non-template) parameter se kya bind hota hai?Sirf rvalues (temporaries); lvalues nahi.
Moved-from object ko reset kyun karna chahiye (jaise pointer ko null set karna)? Double-free / double-delete se bachne ke liye; use valid-but-unspecified chhoda jaana chahiye taaki uska destructor safe ho.
Move constructor ko noexcept kyun mark karein? Taaki std::vector (aur dusre) reallocation ke dauran isse use karein; warna exception safety ke liye woh copying par fall back karte hain.
std::move aur std::forward mein fark?move ek unconditional cast to rvalue hai; forward<T> ek conditional cast hai jo caller ki lvalue/rvalue category preserve karta hai, sirf forwarding references ke saath use hota hai.
Forwarding (universal) reference kya hota hai? Ek T&& jahan T ek deduced template parameter hai; yeh reference collapsing ke through dono lvalues aur rvalues se bind hota hai.
Reference collapsing ke rules? T& &, T& &&, T&& & sab → T&; sirf T&& && → T&&.
return std::move(localVar); zyaadatar buri idea kyun hai?Yeh copy elision/NRVO disable kar deta hai; plain return localVar; already move karta hai ya elide karta hai aur kam se kam utna hi fast hota hai.
void f(Str&& s) ke andar, s lvalue hai ya rvalue?Ek lvalue (uska naam hai). Isse move karne ke liye aapko std::move(s) karna hoga.
Agar kisi type mein move constructor nahi hai, toh std::move(x) kya select karta hai? Copy constructor — silently, bina kisi error ke.
Rule of Five — destructor, copy ctor, copy assign, move ctor, move assign
RAII and Ownership — moves resources ki ownership transfer karte hain
Copy elision and RVO — jab compiler construction bilkul skip kar deta hai
Templates and type deduction — forwarding references ka basis
std::vector internals — reallocation ke dauran moves use karta hai
Reference collapsing — woh rule jo forwarding ko kaam karta hai
noexcept specifier
Deep copy of temporaries wastes work
lvalue: named, has address
rvalue: temporary, expiring
Compiler picks move or copy