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
| Rule | Meaning |
|---|---|
| Rule of Zero | Use members like std::string, vector and smart pointers. Write none of the special functions. The best choice |
| Rule of Three | If you write a destructor, copy constructor or copy assignment, you probably need all three |
| Rule of Five | The 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. |