Насколько я понимаю, в следующей функции выражение foo
в заявлении return foo;
является значением xvalue, потому что объект, который он обозначает, истекает (даже если foo
является lvalue в предыдущих утверждениях):
Foo bar()
{
Foo foo;
change(foo);
return foo;
}
Такая истекающая стоимость не покрывается Какие выражения создают значения xvalue?.
Это изменится в следующем случае?
Foo bar(Foo&& foo)
{
change(foo);
return foo;
}
Является foo
xvalue в операторе возврата? И, в частности, это кандидат на переезд? А для РВО? Или следует использовать return std::move(foo)
?
Я не знаю, каково формальное правило для классификации выражения foo
как xvalue в операторе return первого случая, поэтому я не могу проверить его во втором.
В этой функции foo
является именующий типа «rvalue ссылка на Foo
Msgstr «Когда вы его возвращаете, так как копия должна быть построена (из-за типа возвращаемого значения), вы создаете совершенно новое значение, которое делает bar(...)
prvalue, согласно §3.10.1.5:
Prvalue («чистое» rvalue) — это rvalue, которое не является xvalue. [Пример: результат вызова функции, тип возвращаемой которой не является ссылкой, является предварительным значением. Значение литерала, такого как 12, 7.3e5 или true, также является prvalue. — конец примера]
В связи с тем, что внутри функции, foo
является именующий, выражение return foo
не является кандидатом на строительство и переезд конструктор копирования выбран.
И да, здесь применяется RVO, при условии, что ход не выбран первым. Здесь нет ничего особенного. Согласно §12.8.31:
Такое исключение операций копирования / перемещения, называемое разрешением копирования, допускается при следующих обстоятельствах (которые могут быть объединены для удаления нескольких копий):
[…]
- в операторе возврата в функции с типом возврата класса, когда выражение является именем энергонезависимого автоматического объекта (отличного от параметра функции или оператора catch) с тем же cv-неквалифицированным типом, что и тип возврата функции, операцию копирования / перемещения можно опустить, создав автоматический объект непосредственно в возвращаемое значение функции
Чтобы уточнить, foo
само по себе это lvalue, но утверждение:
return foo;
в конечном итоге результаты (из bar(...)
expression) в prvalue из-за того факта, что, учитывая тот тип возвращаемого значения, выражение эквивалентно:
return Foo(foo);
это означает, что временное значение, скопированное из foo
возвращается из функции bar
,
Перечитывая ответ, он все еще не имеет смысла для меня. Вы говорите: из-за того, что внутри функции foo является lvalue, выражение return foo не является кандидатом на конструкцию перемещения, и выбран конструктор копирования. Почему это верно в одном случае, а не в другом?
При возвращении foo
Вы должны создать новый Foo
значение (потому что вы возвращаете копию) из именующий ссылка foo
, Это делается неявно конструктором копирования. Так return foo;
эквивалентно return Foo(foo)
, При условии foo
является lvalue, выбран конструктор копирования (а не конструктор перемещения).
Теперь, когда у вас есть это новое временное значение (построенный из foo
), само значение, которое выходит из выражения bar(...)
, это prvalue. Итак, когда вы делаете:
auto res = bar(...);
Вы должны построить Foo
скопировать из prvalue. Поскольку prvalue также является rvalue, выбирается конструктор со ссылочным параметром rvalue (конструктор перемещения).