Вопрос о rvalue-ссылке

130
31 мая 2019, 07:40

В очередной раз убедился, что мое понимание ссылок C++ оставляет желать... Вот посмотрим простой пример:

void DoString(string&& str) { }
. . .
DoString("I'm an rvalue string");

Все прекрасно компилируется, как и должно. Теперь попробуем так:

string s { "I'm an lvalue string" };
DoString(s);

Ошибка компиляции (как и должно быть):

error C2664: 'void DoString(std::string &&)': cannot convert argument 1 from 'std::string' to 'std::string &&'
note: You cannot bind an lvalue to an rvalue reference

Теперь попробуем сделать чуть сложнее:

string&& rs = string("I'm an rvalue string");
DoString(rs);

Та же самая ошибка компиляции, что и выше! Не могу сконвертировать аргумент из std::string в std::string && блаблабла. Но ведь rs - это rvalue. И если навести на нее курсор, студия показывает тип std::string &&. Откуда вылезло lvalue?

Была мысль, что это потому, что rs - ссылка на x-value, а не на pr-value. Однако функция std::move тоже возвращает ссылку на x-value, но при этом вызов

DoString(std::move(s));

прекрасно компилируется.

Так что вопросы у меня появились такие:

1) Что происходит при присваивании string&& rs = string("I'm an rvalue string");?

2) Что такое rs и каким образом в виде аргумента функции она превращается в lvalue?

Answer 1

Видимо вас вводит в заблуждения терминолигия С++. Дело в том, что термин rvalue может использоваться для обозначения двух разных, независимых понятий:

  • тип rvalue reference, тип переменной str, объявленной как string && str в вашем примере
  • категория выражения rvalue, результат std::move(s) (xvalue) или string("I'm an rvalue string") (prvalue) в вашем примере

Так вот, чтобы выражение подходило под тип rvalue reference оно должно иметь категорию rvalue, а не тип rvalue reference. В записи DoString(rs); тип rs будет rvalue reference, а категория выражения - нет.

Answer 2

Теперь попробуем сделать чуть сложнее:

string&& rs = string("I'm an rvalue string");
DoString(rs);

Та же самая ошибка компиляции, что и выше! Не могу сконвертировать аргумент из std::string в std::string && блаблабла.

Разница между lvalue-ссылками и rvalue-ссылками в том, какими выражениями их можно инициализировать.

Сами ссылки (обоих видов) всегда считаются lvalue.

Поэтому нам, например, нужен std::forward для универсальных ссылок.

Что происходит при присваивании string&& rs = string("I'm an rvalue string");

Создается временный безымянный объект типа string. В обычных условиях он был бы тут же разрушен, но так как вы инициализировали им ссылку, для него происходит продление времени жизни (lifetime extension). Он будет разрушен вместе со ссылкой.

(Если я правильно помню, временный объект создается как prvalue, а затем за счет temporary meterialization превращается в xvalue. И то, и другое - это подкатегории rvalue.)

Answer 3

rs - это lvalue, вы же сохранили строку в переменной. Смотрите определения тут http://eel.is/c++draft/basic.lval#1

READ ALSO
Рефакторинг кода используя computeIfAbsent()

Рефакторинг кода используя computeIfAbsent()

У меня есть следующий код:

131
Почему JFrame мигает?

Почему JFrame мигает?

Вот такая вот простенькая программ, окно отслеживает позицию мыши и рисует ее координаты рядом с нейНо заметил интересный эффект мигания...

116
Визуализация аудио по ссылке Android Java

Визуализация аудио по ссылке Android Java

Столкнулся с такой проблемой, что не могу одновременно воспроизводить файл по ссылке и визуализировать егоОтдельно визуализация загруженного...

172