Site Tools


wiki:move-semantics

Move semantics

Move semantics let an object transfer ownership of its internal resources (heap buffers, file handles, and the like) to another object instead of duplicating them, when the source object is about to be destroyed or overwritten anyway. Before C++11, returning or reassigning an object always meant a full copy of everything it owned; move semantics let the compiler pick a much cheaper operation, stealing the resource pointer and leaving the source empty, in exactly the cases where making a genuine independent copy was never actually necessary.

std::vector<int> makeVector() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    return v;   // moved out, not copied: v's internal buffer is stolen
}
 
std::vector<int> a = makeVector();   // no deep copy of the buffer happened

Rvalues and rvalue references

The mechanism hinges on distinguishing lvalues (things with a name and a persistent identity, like a local variable) from rvalues (temporaries about to disappear, like a function's return value or the result of std::move). An rvalue reference, written T&&, binds only to rvalues, which is what lets overload resolution pick a move constructor over the ordinary copy constructor whenever the source is a temporary. std::move doesn't move anything by itself; it's purely a cast that tells the compiler “treat this named lvalue as an rvalue,” making it eligible for move overloads even though it still has a name.

struct Buffer {
    Buffer(Buffer &&other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;   // steal the pointer, leave source empty
        other.size = 0;
    }
    int *data;
    size_t size;
};
 
Buffer b1(1000);
Buffer b2 = std::move(b1);   // b2 steals b1's buffer; b1.data is now null

Why the moved-from object still has to be valid

After a move, the source object is guaranteed to be in a “valid but unspecified” state, its invariants still hold (a moved-from vector is still a legal, empty-or-whatever-sized vector), but its actual contents shouldn't be relied on. This matters because the source's destructor still runs later: Buffer's destructor will call delete other.data, and if the move constructor hadn't nulled it out, both the moved-from and moved-to Buffer would eventually try to delete the same pointer, a double free. Every move constructor and move assignment operator has to leave the source in a state its own destructor can safely handle, which is precisely why they null out or reset whatever they stole.

wiki/move-semantics.md · Last modified: by 127.0.0.1