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

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

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

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

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

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

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

13

Решение

Я всегда помню lvalue как значение, которое имеет имя или может быть адресовано. Поскольку x имеет имя, оно передается как lvalue. Цель ссылки на rvalue состоит в том, чтобы позволить функции полностью обрезать значение любым удобным для нее способом. Если мы передадим 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 используется для идеальной пересылки в шаблонах.

8

Другие решения

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

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

Когда имя появляется в выражении, это именующий — даже если это будет ссылка на Rvalue. Это может быть преобразовано в Rvalue если выражение требует этого (т. е. если необходимо его значение); но это не может быть связано с Rvalue ссылка.

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

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

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

10

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

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

9

Rvalue а также именующий категории выражения.

Rvalue ссылка а также lvalue ссылка категории Рекомендации.

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

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

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

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

0
По вопросам рекламы [email protected]