Какая разница между обычной ссылкой rvalue и возвращаемой std :: forward?

Я не могу этого сделать:

int &&q = 7;
int &&r = q; 
//Error Message:
//cannot convert from 'int' to 'int &&'
//You cannot bind an lvalue to an rvalue reference

Если я правильно понял, при инициализации ссылки на rvalue, временная переменная также была инициализирована. Итак, int &&q = 7; может рассматриваться как:

int temp = 7;
int &&q = temp;

И при использовании ссылки с правой стороны, я фактически использую рефери. Итак, int &&r = q; может рассматриваться как:

int &&r = temp;  //bind an lvalue to an rvalue reference, cause error, understandable

Итак, как я понимаю, возникает ошибка компилятора.


Почему добавление std::forward может решить это?

int &&q = 7;
int &&r = std::forward<int>(q);

Я знаю, что std::forward всегда возвращает ссылку rvalue, как ссылка, возвращаемая std::forward отличается от int&&q?

Ответ 1

как ссылка, возвращаемая std::forward отличается от int&&q?

Их категории значений разные. И обратите внимание, что типы и категории значений - это разные вещи.

q - именованная переменная, она квалифицируется как lvalue, поэтому она не может быть привязана к ссылке rvalue.

(акцент мой)

имя переменной, функцию, объект параметра шаблона (начиная с С++ 20) или элемент данных независимо от типа, например std::cin или std::endl. Даже если тип переменной является ссылкой rvalue, выражение, состоящее из его имени, является выражением lvalue;

В то время как ссылка rvalue, возвращаемая функцией, квалифицируется как xvalue, которая принадлежит rvalue.

вызов функции или перегруженное операторное выражение, возвращаемым типом которого является rvalue ссылка на объект, например std::move(x);

Ответ 2

Разница между выражениями q и std::forward<int>(q) заключается в том, что первая является lvalue, а последняя является rvalue (фундаментальной категории xvalue).

Я обратился к подобным проблемам в этом ответе: дело в том, что q как выражение является lvalue, потому что оно имеет имя. std::forward<int>(q) (или эквивалентный std::move(q)) - это выражения, которые не имеют имен, а так как они возвращают (неназванные) ссылки rvalue, они являются значениями x, что является подкатегорией rvalue и, таким образом, может привязываться к ссылке rvalue.