Lvalue и Rvalue
Lvalue — это объект, имеющий определённое местоположение в памяти (имя переменной, разыменованный указатель). Rvalue — это временное значение, не имеющее стабильного адреса.
1int x = 42; // x — lvalue, 42 — rvalue2int* p = &x; // p — lvalue, &x — rvalue3std::string s = "hello"; // s — lvalue4s + " world" // rvalue (временная строка)
Rvalue References
Rvalue reference (T&&) позволяет «перехватить» временный объект.
1void process(int& x) {2 std::cout << "Lvalue reference: " << x << "\n";3}45void process(int&& x) {6 std::cout << "Rvalue reference: " << x << "\n";7}89int main() {10 int a = 10;11 process(a); // Lvalue reference: 1012 process(42); // Rvalue reference: 4213 process(a + 1); // Rvalue reference: 1114}
Move Constructor
Move constructor «перемещает» ресурсы из одного объекта в другой, избегаяdeep copy.
1class Buffer {2 int* data;3 size_t size;4public:5 // Constructor6 Buffer(size_t size) : data(new int[size]), size(size) {}78 // Copy constructor (deep copy)9 Buffer(const Buffer& other) : data(new int[other.size]), size(other.size) {10 std::copy(other.data, other.data + size, data);11 }1213 // Move constructor (transfer ownership)14 Buffer(Buffer&& other) noexcept : data(other.data), size(other.size) {15 other.data = nullptr;16 other.size = 0;17 }1819 // Destructor20 ~Buffer() { delete[] data; }2122 // Copy assignment23 Buffer& operator=(const Buffer& other) {24 if (this != &other) {25 delete[] data;26 data = new int[other.size];27 size = other.size;28 std::copy(other.data, other.data + size, data);29 }30 return *this;31 }3233 // Move assignment34 Buffer& operator=(Buffer&& other) noexcept {35 if (this != &other) {36 delete[] data;37 data = other.data;38 size = other.size;39 other.data = nullptr;40 other.size = 0;41 }42 return *this;43 }44};
std::move
std::move — это каст к rvalue reference. Он не перемещает ничего, а только позволяет компилятору выбрать move-перегрузку.
1std::string s1 = "hello";2std::string s2 = std::move(s1); // Move constructor3// s1 теперь в "moved-from" состоянии45std::vector<int> v1 = {1, 2, 3};6std::vector<int> v2 = std::move(v1); // Move assignment7// v1 теперь пуст
Perfect Forwarding
Perfect forwarding позволяет передавать аргументы через промежуточную функцию, сохраняя их категорию (lvalue/rvalue).
1template <typename T>2void wrapper(T&& arg) {3 // arg — forwarding reference (не rvalue reference!)4 process(std::forward<T>(arg));5}67// std::forward возвращает:8// - lvalue reference, если T — lvalue reference9// - rvalue reference, если T — не reference
▸Реальный пример
1template <typename T, typename... Args>2std::unique_ptr<T> make_unique_custom(Args&&... args) {3 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));4}56auto ptr = make_unique_custom<MyClass>(1, "hello", 3.14);
Move-only типы
1class NonCopyable {2 std::unique_ptr<int> data;3public:4 NonCopyable(int value) : data(std::make_unique<int>(value)) {}56 // Только move, без copy7 NonCopyable(NonCopyable&&) = default;8 NonCopyable& operator=(NonCopyable&&) = default;910 NonCopyable(const NonCopyable&) = delete;11 NonCopyable& operator=(const NonCopyable&) = delete;12};
std::move и контейнеры
1std::vector<std::string> v1 = {"hello", "world"};2std::vector<std::string> v2 = std::move(v1); // Move assignment34// Элементы перемещены, v1 пуст5// Вместо deep copy всех строк
Заключение
Move semantics — это ключ к производительности в Modern C++. Они позволяют избегать глубокого копирования, перемещая ресурсы вместо их дублирования. Используйте std::move для передачи владения и perfect forwarding для generic-функций.