thecodex.expert · The Codex Family of Knowledge
C++

Move Semantics

std::move() doesn’t actually move anything by itself — it just casts its argument to an rvalue reference, making it ELIGIBLE to be moved from if a move constructor exists.

C++20 / C++23 std::move() doesn’t actually move anything Last verified:
Canonical Definition

Copying a large object (a std::vector with a million elements, for instance) means allocating new memory and copying every element — genuinely expensive. A move constructor instead just steals the source's internal pointer and resets the source to an empty state, an operation that's nearly free regardless of how much data is involved. The compiler automatically prefers moving over copying for temporaries (rvalues) that are about to be destroyed anyway. std::move() itself performs no actual moving — it's purely a cast that marks its argument as an rvalue reference, making it eligible for the move constructor to be selected, rather than the copy constructor.

Copying vs moving: a real cost difference

b's constructor copies all million elements; c's constructor just steals a's pointer, leaving a empty — identical resulting data, radically different cost.

C++copy_vs_move.cpp
std::vector<int> a(1000000, 42);   // a million ints

std::vector<int> b = a;                // COPY — allocates new memory, copies everything
std::vector<int> c = std::move(a);   // MOVE — steals a's pointer, nearly free

std::cout << a.size() << "\n";   // 0 — a was moved FROM, now valid but empty

Writing a move constructor

The move constructor takes T&& (an rvalue reference), steals the source's pointer directly, and sets the source's pointer to nullptr so its destructor won't accidentally delete memory this object now owns.

C++move_constructor.cpp
class Buffer {
    int* data;
    size_t size;
public:
    // move constructor
    Buffer(Buffer&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;   // prevent other's destructor from deleting OUR memory
        other.size = 0;
    }

    ~Buffer() { delete[] data; }   // safe even if data is nullptr — delete[] on nullptr is a no-op
};

std::move doesn't move anything by itself

std::move is purely a cast — it doesn't move data, allocate anything, or call any function. It just tells the compiler "treat this as an rvalue," making the move constructor a candidate for overload resolution instead of the copy constructor.

C++move_is_just_a_cast.cpp
std::string s = "hello";
std::string s2 = std::move(s);   // std::move just casts s to std::string&&
// after this line, s is in a valid but unspecified state — don't rely on its contents

Sources

1
ISO/IEC 14882 (C++ Standard), "Move constructors" and "std::move," cppreference.com/w/cpp/language/move_constructor.