Home / Forums / Mastering Move Semantics, Rvalue References (&&) and std::move in Modern C++

UnreliableCode Community

Developer Research, Reverse Engineering & Coding Community

Tutorial

Mastering Move Semantics, Rvalue References (&&) and std::move in Modern C++

KronoDev
C++ / Game Modder
MEMBER
Rep: 195
Join Date: Jan 2023
Posts: 32
Thanks: 48
2y ago · Mar 10, 2024 2:20 PM
#1
Move semantics (introduced in C++11 and refined in C++14/17/20) is one of the most important concepts for writing high-performance C++ software by eliminating unnecessary deep copies.

1. Lvalues vs Rvalues:
- Lvalue (Left-hand value): An expression that has an identifiable memory location and persists beyond a single expression (e.g. named variables
CODE
int x = 10;
).
- Rvalue (Right-hand value): A temporary value that does not persist beyond the expression that creates it (e.g. literals
CODE
42
, function returns
CODE
std::string("temp")
).

2. Implementing the Move Constructor & Move Assignment Operator:
CPP
#include <iostream>
#include <utility>
#include <cstring>

class DynamicBuffer {
private:
    char* data;
    size_t size;

public:
    // Constructor
    DynamicBuffer(size_t s) : size(s), data(new char[s]) {
        std::memset(data, 0, size);
    }

    // Destructor (RAII)
    ~DynamicBuffer() {
        delete[] data;
    }

    // Copy Constructor (Deep Copy - O(N))
    DynamicBuffer(const DynamicBuffer& other) : size(other.size), data(new char[other.size]) {
        std::memcpy(data, other.data, size);
        std::cout << "[Copy Constructor] Deep copy performed
";
    }

    // Move Constructor (Shallow Resource Transfer - O(1))
    DynamicBuffer(DynamicBuffer&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr; // Leave source object in valid empty state
        other.size = 0;
        std::cout << "[Move Constructor] Pointer ownership stolen (O(1))
";
    }

    // Move Assignment Operator
    DynamicBuffer& operator=(DynamicBuffer&& other) noexcept {
        if (this != &other) {
            delete[] data; // Free existing resource
            data = other.data;
            size = other.size;
            other.data = nullptr;
            other.size = 0;
            std::cout << "[Move Assignment] Resource swapped
";
        }
        return *this;
    }
};


3. What
CODE
std::move
actually does:

CODE
std::move
does not move anything at runtime! It is simply an unconditional
CODE
static_cast<T&&>
that converts an lvalue into an rvalue reference, allowing the compiler to select the move constructor over the copy constructor.

CPP
DynamicBuffer buf1(1024 * 1024); // 1MB buffer
DynamicBuffer buf2 = std::move(buf1); // Calls move constructor, zero memory allocation!
KronoDev - Keep coding, keep learning
The following users thanked KronoDev for this post:
HexRays99
Senior Reverse Engineer
VIP
Rep: 380
Join Date: Apr 2022
Posts: 36
Thanks: 94
2y ago · Mar 10, 2024 4:45 PM
#2
Crucial reminder: always mark move constructors and move assignment operators with
CPP
noexcept
!

If a move constructor is not marked
CPP
noexcept
, standard library containers like
CPP
std::vector
will default to calling the slow copy constructor during reallocations to maintain the strong exception safety guarantee.
HexRays99 · Reverse Engineering & Static Analysis
CPP
// Always check your pointers!
if (!pLocalPlayer) return;
ZeroMemory
Member
MEMBER
Rep: 75
Join Date: Oct 2024
Posts: 32
Thanks: 18
2y ago · Mar 11, 2024 9:15 AM
#3
Originally Posted by @KronoDev
std::move does not move anything at runtime! It is simply an unconditional static_cast<T&&>

This explanation finally clicked for me! The difference between stealing the raw pointer and deep-copying is huge for performance.
ZeroMemory · Always experimenting