Move-семантика и rvalue refs
Тема дорожной карты · Modern C++ Developer
Move-семантика — фундаментальная фича C++11, позволяющая «передвигать» содержимое объекта вместо копирования. Для тяжёлых типов (vector с миллионом элементов, unique_ptr с большим ресурсом) копирование стоит O(N), а move — O(1): передача владения указателей и обнуление источника. Базируется на новой категории ссылок — rvalue references (T&&), которые могут привязываться только к временным выражениям, обеспечивая статическую гарантию «этот объект сейчас умрёт, забирайте у него ресурсы».
Как это работает
C++11 ввёл move constructor (T(T&&)) и move assignment (T& operator=(T&&)) в дополнение к copy-аналогам. Компилятор автоматически генерирует их по rule-of-five (если ни один из пяти специальных методов не пользовательский). При возврате локального объекта из функции, передаче временного значения в параметр и явном std::move(obj) вызывается move-версия. std::move — это просто cast в rvalue reference, не «движение». Forwarding reference (T&& в template-контексте) — collapse-правила: T&& где T = U& даёт U&, иначе U&&. std::forward<T>(arg) корректно прокидывает «крестную» категорию (lvalue или rvalue) дальше — обычно в perfect forwarding (emplace_back, factory-функции).
Когда применять
Move-семантика работает автоматически для всех STL-контейнеров — пишите vec.push_back(std::move(big_string)) чтобы избежать копии. Реализуйте свой move-конструктор для классов, владеющих ресурсами (file handles, network connections, кастомные allocators). Используйте std::move при передаче владения наружу (return значения, передача в callback). Perfect forwarding (emplace_back, make_unique, factory) — стандартная техника в обобщённом коде.
Типичные ошибки
Использование объекта после std::move(x) — UB или unspecified state (зависит от типа). Возврат std::move(local) из функции — отключает NRVO (Named Return Value Optimization), теряя bonus оптимизации. const T&& — бессмысленно, нельзя писать в const-объект; используйте T&&. Игнорирование noexcept на move-конструкторах — std::vector при growth не сможет двигать элементы и копирует (10x медленнее). Реализация move-семантики без обнуления источника — double-free на destructor-е.