Это очень распространенная ошибка, когда люди впервые узнают о ссылках rvalue. Основная проблема заключается в путанице между типом и категорией значений.
int
— это тип. int&
- это другой тип. int&&
— это еще один тип. Это все разные виды.
lvalue и rvalue — это вещи, называемые категориями значений. Пожалуйста, ознакомьтесь с фантастической диаграммой здесь: Что такое glvalues и prvalues?. Вы можете видеть, что в дополнение к lvalue и rvalue у нас также есть prvalues и glvalues и xvalues, и они формируют различное отношение типа диаграммы Венна.
В C++ есть правила, согласно которым переменные различных типов могут связываться с выражениями. Однако ссылочный тип выражения отбрасывается (часто говорят, что выражения не имеют ссылочного типа). Вместо этого у выражения есть категория значения, которая определяет, какие переменные могут к нему привязываться.
Другими словами: ссылки rvalue и ссылки lvalue имеют непосредственное отношение только к левой части присваивания, когда переменная создается/привязывается. С правой стороны мы говорим о выражениях, а не о переменных, а ссылка на rvalue/lvalue актуальна только в контексте определения категории значений.
Очень простой пример для начала — это просто посмотреть на вещи чисто типа int
. Переменная типа int
как выражение является lvalue. Однако выражение, состоящее из вычисления функции, которая возвращает int
, является rvalue. Это имеет интуитивно понятный смысл для большинства людей; ключевой момент, однако, состоит в том, чтобы отделить тип выражения (даже до того, как ссылки будут отброшены) и его категорию значения.
Это приводит к тому, что хотя переменные типа int&&
могут связываться только со значениями r, это не означает, что все выражения с типом int&&
являются значениями r. Фактически, согласно правилам на http://en.cppreference.com/w/cpp/language/value_category скажем, любое выражение, состоящее из имени переменной, всегда является lvalue, независимо от типа.
Вот почему вам нужно std::move
для передачи ссылок rvalue в последующие функции, которые принимают ссылку rvalue. Это связано с тем, что ссылки rvalue не связываются с другими ссылками rvalue. Они связываются с rvalue. Если вы хотите получить конструктор перемещения, вам нужно дать ему rvalue для привязки, а именованная ссылка rvalue не является rvalue.
std::move
— это функция, которая возвращает ссылку на значение rvalue. И какова ценностная категория такого выражения? Значение? Неа. Это xvalue. Это в основном rvalue с некоторыми дополнительными свойствами.
person
Nir Friedman
schedule
22.08.2017
foo
объект, обозначенныйa
, является локальным для функции, поэтому гарантируется, что срок действия объекта истекает и его можно безопасно перемещать. Вbar, nothing is known about the object designated by
a`, поэтому вы не хотите изменять его молча. Помните, что ссылки rvalue — это еще один вид ссылок; основной язык не имеет мнения о том, для чего вы его используете, и не имеет никаких ожиданий относительно времени жизни или псевдонимов. - person Kerrek SB   schedule 22.08.2017a
вfoo
поведением реализации? Мне не удалось найти какое-либо соответствующее описание этого поведения в стандарте. - person songyuanyao   schedule 22.08.2017a
гарантированно истечет, как толькоfoo
выйдет за пределы области действия, вызывается конструктор перемещения. - person Jacob Pollack   schedule 22.08.2017a
(вfoo
) является lvalue, тогда следует вызвать конструктор копирования; это все, что я нашел в стандарте. Я не могу найти никаких цитат о том, что если срок действияa
истекает, то вместо этого можно использовать конструктор перемещения. Вот почему я предполагаю, что это поведение реализации, не гарантированное стандартом. - person songyuanyao   schedule 22.08.2017