Он объявляет rvalue ссылку (doc предложение по стандартам).
Вот введение в 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, как временное, без необходимости быть const. Таким образом, этот синтаксис теперь допустим:
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
. Это означает, что если factory вызывается с 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&)
Важные свойства rvalue-ссылки :
- Для разрешения перегрузки lvalues предпочитают привязку к ссылкам lvalue иrvalues предпочитает привязку к ссылкам rvalue .Поэтому временные производители предпочитают вызывать конструктор перемещения / оператор присваивания перемещения по сравнению с конструктором копирования / оператором присваивания.
- ссылки на rvalue будут неявно связываться со значениями r и временными значениями, которые являются результатом неявного преобразования ,то есть
float f = 0f; int&& i = f;
правильно сформирован, потому что float неявно конвертируется в int;ссылка будет на временное значение, являющееся результатом преобразования. - Именованные ссылки на rvalue являются lvalues.Безымянные ссылки на rvalue являются rvalues. Важно понимать, почему вызов
std::move
необходим в: foo&& r = foo(); foo f = std::move(r);