Он объявляет ссылку на rvalue (стандартное предложение doc).
Вот введение в rvalue ссылки .
Вот фантастический взгляд в глубину на RValue ссылок по одной из стандартных библиотек Microsoft, разработчиков .
ВНИМАНИЕ: связанная статья по MSDN («Ссылки на Rvalue: функции C ++ 0x в VC10, часть 2») является очень четким введением в ссылки на Rvalue, но содержит утверждения о ссылках на Rvalue, которые когда-то были верны в проекте C ++ 11 стандартная, но не верная для финальной! В частности, в различных точках говорится, что ссылки на rvalue могут связываться с lvalue, что когда-то было истинно, но было изменено (например, int x; int && rrx = x; больше не компилируется в GCC) - drewbarbs 13 июля '14 в 16:12
Самым большим отличием ссылки C ++ 03 (теперь называемой ссылкой lvalue в C ++ 11) является то, что она может связываться с rvalue как временная, не будучи константой. Таким образом, этот синтаксис теперь допустим:
T&& r = T();
rvalue ссылки в первую очередь предусматривают следующее:
Переместить семантику . Теперь можно определить конструктор перемещения и оператор присваивания перемещения, который принимает ссылку rvalue вместо обычной ссылки const-lvalue. Перемещение функционирует как копия, за исключением того, что оно не обязано сохранять источник без изменений; фактически, он обычно изменяет источник так, что он больше не владеет перемещенными ресурсами. Это отлично подходит для устранения посторонних копий, особенно в стандартных реализациях библиотеки.
Например, конструктор копирования может выглядеть так:
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}
Если этот конструктор был передан временный, копия будет ненужной, потому что мы знаем, что временный будет просто уничтожен; почему бы не использовать ресурсы, временно выделенные? В C ++ 03 нет способа предотвратить копирование, так как мы не можем определить, были ли мы переданы временно. В C ++ 11 мы можем перегрузить конструктор перемещения:
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}
Обратите внимание на большую разницу: конструктор перемещения фактически изменяет свой аргумент. Это эффективно «переместит» временный объект в создаваемый объект, тем самым устраняя ненужную копию.
Конструктор перемещения будет использоваться для временных значений и для неконстантных lvalue-ссылок, которые явно преобразуются в rvalue-ссылки с использованием std::move
функции (он просто выполняет преобразование). Следующий код вызывает конструктор перемещения для f1
и f2
:
foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"
Идеальная пересылка . rvalue ссылки позволяют нам правильно пересылать аргументы для шаблонных функций. Возьмем, к примеру, эту заводскую функцию:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}
Если мы вызвали factory<foo>(5)
, аргумент будет выведен как int&
, который не будет привязан к литералу 5, даже если foo
конструктор принимает int
. Что ж, мы могли бы вместо этого использовать A1 const&
, но что если foo
принять аргумент конструктора по неконстантной ссылке? Для того, чтобы действительно обобщенная функция фабрики, мы должны перегрузить завод по A1&
и A1 const&
. Это может быть хорошо, если фабрика принимает 1 тип параметра, но каждый дополнительный тип параметра умножает необходимую перегрузку, установленную на 2. Это очень быстро не поддерживается.
Ссылки на rvalue решают эту проблему, позволяя стандартной библиотеке определять std::forward
функцию, которая может правильно пересылать ссылки на lvalue / rvalue. Для получения дополнительной информации о том std::forward
, как работает, посмотрите этот отличный ответ .
Это позволяет нам определить фабричную функцию следующим образом:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}
Теперь аргумент rvalue / lvalue-ness сохраняется при передаче в T
конструктор. Это означает, что если фабрика вызывается с помощью rvalue, то T
конструктор вызывается с помощью rvalue. Если factory вызывается с lvalue, то T
конструктор вызывается с lvalue. Улучшенная фабричная функция работает благодаря одному специальному правилу:
Когда тип параметра функции имеет форму, T&&
где T
есть параметр шаблона, а аргумент функции является lvalue типа A
, тип A&
используется для вывода аргумента шаблона.
Таким образом, мы можем использовать фабрику так:
auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1); // calls foo(foo const&)
Важные справочные свойства :
- Для разрешения перегрузки lvalue предпочитает привязку к ссылкам lvalue, а rvalues предпочитает привязку к ссылкам rvalue . Следовательно, временные производители предпочитают вызывать конструктор перемещения / оператор присваивания перемещения по сравнению с конструктором копирования / оператором присваивания.
- Ссылки на rvalue будут неявно связываться с rvalue и временными значениями, которые являются результатом неявного преобразования . т.е.
float f = 0f; int&& i = f;
хорошо сформирован, потому что float неявно конвертируется в int; ссылка была бы на временную, которая является результатом преобразования.
- Именованные ссылки на rvalue являются lvalues. Безымянный rvalue ссылки являются rvalues. Это важно, чтобы понять, почему
std::move
вызов необходим в:foo&& r = foo(); foo f = std::move(r);