move semantics
Новая категория ссылок, которая связывается только с временными объектами и явно помеченными как «можно забрать» значениями. Фундамент, на котором построены move semantics и perfect forwarding.A new reference category that binds only to temporary objects and to values explicitly marked as "safe to steal from." The foundation underlying move semantics and perfect forwarding.
T&& — ссылка на rvalue: она связывается с временными объектами (результатами выражений, не имеющими имени) и с объектами, явно приведёнными к rvalue через std::move. До C++11 в языке существовала только одна категория ссылок — T& (теперь называемая lvalue reference), которая не могла связаться с временным объектом напрямую (кроме const T&, что лишь продлевало жизнь временного объекта, не позволяя его модифицировать).T&& is a reference to an rvalue: it binds to temporary objects (unnamed results of expressions) and to objects explicitly cast to rvalue via std::move. Before C++11 the language had only one reference category — T& (now called an lvalue reference) — which couldn't bind to a temporary directly (except const T&, which only extended the temporary's lifetime without allowing it to be modified).
void f(std::string& s); // не примет временный// won't accept a temporary
void f(const std::string& s); // примет, но нельзя менять/забрать// accepts it, but can't modify/steal from it
f(std::string("temp")); // ошибка для первой f,// error for the first f,
// вторая — работает, но не может «украсть» буфер// the second works, but can't "steal" the buffer
void f(std::string&& s); // связывается с временным,// binds to a temporary,
// s можно безопасно «выпотрошить»// s can be safely "gutted"
f(std::string("temp")); // выбирает эту перегрузку// selects this overload
До rvalue references язык не мог отличить «временный объект, который всё равно исчезнет» от «именованной переменной, которая ещё используется»:Before rvalue references, the language couldn't distinguish "a temporary object that's about to disappear anyway" from "a named variable that's still in use":
Временный объект (например, возврат функции по значению) гарантированно будет уничтожен сразу после выражения — забрать его внутренние ресурсы (буфер, дескриптор) вместо копирования безопасно. Без отдельной категории ссылок компилятор не мог выразить это различие на уровне перегрузки.A temporary object (say, a function's by-value return) is guaranteed to be destroyed right after the expression — stealing its internal resources (buffer, handle) instead of copying them is safe. Without a separate reference category, the compiler had no way to express this distinction at the overload level.
Возврат тяжёлых объектов (векторов, строк) по значению до move semantics означал копирование при каждом возврате (RVO помогало не всегда) — rvalue references делают такой возврат дешёвым перемещением без изменения сигнатур пользовательского кода.Returning heavy objects (vectors, strings) by value before move semantics meant a copy on every return (RVO didn't always help) — rvalue references turn that return into a cheap move without changing any user-code signatures.
Universal reference (T&& в контексте вывода типа шаблона) нужна, чтобы шаблонная функция-обёртка могла принять аргумент любой категории значений и передать его дальше, сохранив исходную категорию — задача, нерешаемая без rvalue references.A universal reference (T&& in a template type-deduction context) is needed so a template wrapper function can accept an argument of any value category and forward it onward while preserving that category — a problem with no solution without rvalue references.
C++11 формализует систему категорий значений (value categories): каждое выражение — lvalue, xvalue или prvalue (вместе xvalue и prvalue образуют rvalue). Правила перегрузки функций расширены — компилятор выбирает T&&-перегрузку для rvalue-выражений и T&/const T& для lvalue. Физически rvalue reference — обычная ссылка на уровне ассемблера (указатель); отличие целиком в правилах разрешения перегрузок на этапе компиляции.C++11 formalizes the system of value categories: every expression is an lvalue, xvalue, or prvalue (xvalue and prvalue together form rvalue). Overload resolution rules were extended — the compiler picks the T&& overload for rvalue expressions and T&/const T& for lvalues. Physically, an rvalue reference is an ordinary reference at the assembly level (a pointer); the difference is entirely in the compile-time overload resolution rules.
void f(const std::string&); // #1 — принимает и lvalue, и rvalue// #1 — accepts both lvalue and rvalue
void f(std::string&&); // #2 — принимает только rvalue// #2 — accepts only rvalue
std::string s = "hi";
f(s); // выбирает #1 — s именованная переменная, lvalue// selects #1 — s is a named variable, an lvalue
f(std::move(s)); // выбирает #2 — std::move приводит s к rvalue-ссылке// selects #2 — std::move casts s to an rvalue reference
f(s + "!"); // выбирает #2 — результат operator+ — временный, prvalue// selects #2 — the result of operator+ is a temporary, a prvalue
// именованная rvalue-ссылка сама по себе — lvalue-выражение внутри функции:// a named rvalue reference is itself an lvalue expression inside the function:
void g(std::string&& s) {
// s как выражение — lvalue! чтобы передать её дальше как rvalue,// s as an expression is an lvalue! to pass it onward as an rvalue,
// нужен явный std::move(s) (см. move-semantics.html)// an explicit std::move(s) is needed (see move-semantics.html)
}
Внутри тела функции параметр типа T&& имеет имя и потому является lvalue-выражением, даже если тип ссылки — rvalue reference. Чтобы передать его дальше с сохранением rvalue-природы, нужен явный std::move — забытый move здесь превращает предполагаемое перемещение в копирование без единой ошибки компиляции.Inside a function body, a parameter of type T&& has a name and is therefore an lvalue expression, even though its reference type is an rvalue reference. Forwarding it onward while preserving its rvalue nature requires an explicit std::move — forgetting that move silently turns an intended move into a copy, with no compiler error at all.
В невыведенном контексте (void f(std::string&&)) — это настоящая rvalue reference. Но в контексте вывода типа шаблона (template<class T> void f(T&& x)) — это universal (forwarding) reference, которая может связаться и с lvalue, и с rvalue в зависимости от аргумента. Путаница этих двух случаев — частая ошибка при чтении чужого шаблонного кода.In a non-deduced context (void f(std::string&&)), it's a genuine rvalue reference. But in a template type-deduction context (template<class T> void f(T&& x)), it's a universal (forwarding) reference, which can bind to either an lvalue or an rvalue depending on the argument. Confusing the two is a common mistake when reading someone else's template code.
Как и const T&, rvalue reference продлевает жизнь временного объекта до конца своего скоупа, но не дольше — возврат такой ссылки из функции (return по rvalue-ссылке на локальный временный) создаёт dangling reference.Like const T&, an rvalue reference extends a temporary's lifetime to the end of its own scope, but no further — returning such a reference from a function (returning an rvalue reference to a local temporary) creates a dangling reference.