Move Semantics and Rvalue References in C++

Move semantics is a C++11 feature that allows the efficient transfer of resources from one object to another without copying. This dramatically improves performance when working with large objects like strings, vectors, and custom classes that manage heap memory.

Lvalue vs Rvalue

To understand move semantics, two fundamental concepts must be clear:

  • Lvalue — An object with a persistent identity and an address in memory. Can appear on the left side of =. Example: a variable int x = 5x is an lvalue.
  • Rvalue — A temporary value with no persistent address. Typically on the right side of =. Example: the literal 5, or the result of a + b.
int x = 10;       // x is lvalue; 10 is rvalue
int y = x + 5;    // x+5 is a temporary rvalue

Rvalue References — &&

C++11 introduced rvalue references (T&&) to detect and bind to temporary values, enabling efficient resource transfer.

int&&  rref = 42;          // rvalue reference to temporary
string&& sr = "Hello";    // rvalue reference to temporary string

The Copy Problem

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v1 = {1, 2, 3, 4, 5};
    vector<int> v2 = v1;   // COPY — expensive for large vectors
    cout << "v1 size: " << v1.size() << endl;   // 5
    cout << "v2 size: " << v2.size() << endl;   // 5
    return 0;
}

Copying a large vector duplicates all its data in memory. When v1 is no longer needed, this wasted effort. Move semantics solves this.

Moving Resources with std::move

vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = move(v1);   // MOVE — no copy, just transfer ownership

cout << "v1 size: " << v1.size() << endl;   // 0 — v1 is empty now
cout << "v2 size: " << v2.size() << endl;   // 5 — v2 has the data

After move(), v1 is in a valid but unspecified (usually empty) state. The data was transferred, not duplicated.

Move Constructor and Move Assignment Operator

For custom classes, a move constructor and move assignment operator must be defined to enable efficient moving:

#include <iostream>
using namespace std;

class Buffer {
public:
    int* data;
    int size;

    Buffer(int n) : size(n), data(new int[n]) {
        cout << "Constructed (size=" << n << ")
";
    }

    // Copy constructor — expensive
    Buffer(const Buffer& other) : size(other.size), data(new int[other.size]) {
        copy(other.data, other.data + size, data);
        cout << "Copied
";
    }

    // Move constructor — cheap
    Buffer(Buffer&& other) noexcept : size(other.size), data(other.data) {
        other.data = nullptr;
        other.size = 0;
        cout << "Moved
";
    }

    ~Buffer() { delete[] data; }
};

int main() {
    Buffer b1(100);
    Buffer b2 = move(b1);   // calls move constructor, not copy
    return 0;
}

Output:

Constructed (size=100)
Moved

Move Assignment Operator

Buffer& operator=(Buffer&& other) noexcept {
    if (this != &other) {
        delete[] data;         // free existing resource
        data = other.data;     // steal other's resource
        size = other.size;
        other.data = nullptr;  // leave other in valid state
        other.size = 0;
    }
    return *this;
}

Perfect Forwarding with std::forward

In template code, std::forward preserves the value category (lvalue/rvalue) of arguments when passing them to another function:

template <typename T>
void wrapper(T&& arg) {
    process(forward<T>(arg));   // preserves lvalue/rvalue nature
}

When is Move Used Automatically?

  • Returning a local variable from a function (NRVO / implicit move)
  • Passing a temporary to a function expecting an rvalue reference
  • Explicitly calling std::move()

Rule of Five

If a class manages resources (like heap memory), it should define all five special functions:

  1. Destructor
  2. Copy constructor
  3. Copy assignment operator
  4. Move constructor
  5. Move assignment operator

Key Takeaways

  • Move semantics transfer ownership of resources instead of copying them — much faster.
  • Rvalue references (T&&) bind to temporary objects.
  • std::move() explicitly converts an lvalue into an rvalue reference to enable moving.
  • Custom classes with heap resources should implement a move constructor and move assignment operator.
  • After moving, the source object is left in a valid but empty state.

Leave a Comment

Your email address will not be published. Required fields are marked *