Подтвердить что ты не робот

Понимание ссылок rvalue

Я думаю, что есть что-то, что я не совсем понимаю о ссылках на rvalue. Почему следующие команды не скомпилируются (VS2012) с ошибкой 'foo' : cannot convert parameter 1 from 'int' to 'int &&'?

void foo(int &&) {}
void bar(int &&x) { foo(x); };

Я бы предположил, что тип int && будет сохранен при передаче из bar в foo. Почему он преобразуется в int один раз внутри тела функции?

Я знаю, что ответ заключается в использовании std::forward:

void bar(int &&x) { foo(std::forward<int>(x)); }

так что, возможно, я просто не понимаю, почему. (Кроме того, почему бы не std::move?)

4b9b3361

Ответ 1

Я всегда помню lvalue как значение, которое имеет имя или может быть адресовано. Поскольку x имеет имя, оно передается как lvalue. Цель ссылки на rvalue - разрешить функции полностью clobber-значение любым способом, который он считает нужным. Если мы передадим x по ссылке, как в вашем примере, тогда у нас нет способа узнать, безопасно ли это сделать:

void foo(int &&) {}
void bar(int &&x) { 
    foo(x); 
    x.DoSomething();   // what could x be?
};

Выполнение foo(std::move(x)); явно сообщает компилятору, что вы закончили с x и больше не нуждаетесь в нем. Без этого движения с существующим кодом могут случиться плохие вещи. std::move является защитой.

std::forward используется для идеальной пересылки в шаблонах.

Ответ 2

Почему он преобразуется в int один раз внутри тела функции?

Это не так; это все еще ссылка на rvalue.

Когда в выражении появляется имя, это значение lvalue - даже если оно является ссылкой на rvalue. Он может быть преобразован в rvalue, если выражение требует (то есть, если его значение необходимо); но он не может быть привязан к ссылке rvalue.

Итак, как вы говорите, чтобы привязать его к другой ссылке rvalue, вы должны явно преобразовать ее в неназванное rvalue. std::forward и std::move - это удобные способы сделать это.

Кроме того, почему бы не std::move?

Почему не так? Это будет иметь больше смысла, чем std::forward, который предназначен для шаблонов, которые не знают, является ли аргумент ссылкой.

Ответ 3

Это правило no name. Внутри bar, x имеет имя... x. Так что теперь это lvalue. Передача чего-либо функции в качестве ссылки rvalue не делает ее значением внутри функции.

Если вы не видите, почему так должно быть, спросите себя: что после foo возвращается x? (Помните, foo может свободно перемещаться x.)

Ответ 4

rvalue и lvalue являются категориями выражений.

Ссылка rvalue и ссылка lvalue являются категориями ссылок.

Внутри объявления, T x&& = <initializer expression>, переменная x имеет тип T && & amp;, и она может быть связана с выражением (the), которое является выражением rvalue. Таким образом, T & был назван ссылочным типом rvalue, потому что он ссылается на выражение rvalue.

Внутри объявления T x& = <initializer expression> переменная x имеет тип T & и ее можно связать с выражением(), которое является выражением lvalue (++). Таким образом, T & был назван ссылочным типом lvalue, потому что он может ссылаться на выражение lvalue.

Затем в C++ важно провести различие между именованием сущности, которая появляется внутри объявления, и тем, когда это имя появляется внутри выражения.

Когда имя появляется внутри выражения, как в foo(x), само имя x является выражением, называемым id-выражением. По определению, id-выражение всегда является выражением lvalue, и выражения lvalue не могут быть связаны со ссылкой на rvalue.