C++ Move Semantics and the Rule of Five

Copying a big object (like a vector of a million items) is slow. If the old object is not needed anymore, C++11 lets you move its data to the new object: it just takes over the pointer.

lvalue and rvalue

  • An lvalue has a name and an address (x).
  • An rvalue is a temporary without a name (x + 1, the result of a function).
  • T&& is an rvalue reference. It can bind to temporaries.

Copy and move in a class

#include <cstring>
#include <iostream>
#include <utility>

class Buffer {
public:
    explicit Buffer(std::size_t n) : size(n), data(new char[n]) { std::cout << "construct " << n << '\n'; }
    ~Buffer() { delete[] data; }

    Buffer(const Buffer& o) : size(o.size), data(new char[o.size]) {          // copy: allocates and copies
        std::memcpy(data, o.data, size);
        std::cout << "copy\n";
    }
    Buffer(Buffer&& o) noexcept : size(o.size), data(o.data) {                // move: takes the pointer
        o.size = 0;
        o.data = nullptr;
        std::cout << "move\n";
    }
    Buffer& operator=(const Buffer& o) {
        if (this != &o) { Buffer tmp(o); std::swap(size, tmp.size); std::swap(data, tmp.data); }
        return *this;
    }
    Buffer& operator=(Buffer&& o) noexcept {
        if (this != &o) { delete[] data; size = o.size; data = o.data; o.size = 0; o.data = nullptr; }
        return *this;
    }
    std::size_t length() const { return size; }
private:
    std::size_t size;
    char* data;
};

Buffer make() { return Buffer(100); }

int main() {
    Buffer a(10);
    Buffer b = a;                    // copy
    Buffer c = std::move(a);         // move: a is now empty
    std::cout << a.length() << ' ' << c.length() << '\n';
    Buffer d = make();               // the temporary is created directly in d (copy elision)
    std::cout << d.length() << '\n';
}
Output

construct 10
copy
move
0 10
construct 100
100

std::move does not move anything. It only changes the expression to an rvalue, so the move constructor can be chosen. After a move, the old object is valid but empty: do not use its value.

The rules of 0, 3 and 5

RuleMeaning
Rule of ZeroUse members like std::string, vector and smart pointers. Write none of the special functions. The best choice
Rule of ThreeIf you write a destructor, copy constructor or copy assignment, you probably need all three
Rule of FiveThe three plus the move constructor and move assignment

Moving is automatic for the standard types

#include <iostream>
#include <string>
#include <utility>
#include <vector>

int main() {
    std::vector<std::string> names;
    std::string long_name(50, 'x');
    names.push_back(long_name);                 // copy
    names.push_back(std::move(long_name));      // move: no copy of the 50 characters
    std::cout << names.size() << ' ' << long_name.size() << '\n';
}
Output

2 0

Note: Mark the move operations noexcept, so containers like vector use them. And prefer the Rule of Zero. Next: Modern C++ Features: ranges, optional, variant and format.

Congratulations! Chapter Finished. Learn more about the similar topics:
Exercises & Assignments
No Content Found.
Interview Questions & Answers
No Content Found.