2015-07-20 2 views
2

Я понимаю, что, в следующей функции, выражение foo в заявлении return foo; является xvalue, так как объект он обозначает это истекающее (даже если foo является именующим в предыдущих заявлениях):Является ли ссылочным параметром rvalue, возвращаемым значением xvalue?

Foo bar() 
{ 
    Foo foo; 
    change(foo); 
    return foo; 
} 

Такой истекающее значение не покрывается What expressions create xvalues?.

Изменилось ли это в следующем случае?

Foo bar(Foo&& foo) 
{ 
    change(foo); 
    return foo; 
} 

Есть foo xvalue в операторе возврата? И, в частности, это кандидат на переезд? А для RVO? Или нужно использовать return std::move(foo)?

Я не знаю, каково формальное правило для классификации выражения foo как xvalue в операторе return первого случая, поэтому я не могу его проверить во втором.

+1

Возможный дубликат [Какие выражения создают значения xvalues?] (Http://stackoverflow.com/questions/11581903/what-expressions-create-xvalues) –

+0

Я не думаю, что это дубликат, так как я спрашиваю о очень частный случай – ricab

+1

Выбранный ответ начинается следующим образом: «Выражение представляет собой значение xvalue, если оно: • результат вызова функции, неявно или явно, тип возвращаемого значения которой является ссылкой на тип объекта». Функция возвращает значение, а не ссылку или значение rvalue. Таким образом, ваша функция не возвращает значение xvalue, и на ваш вопрос будет дан ответ. Просить о очень конкретном случае, который уже рассмотрен в комплексном ответе, кажется, все еще считается дубликатом для меня. –

ответ

2

В этой функции foo является lvalue типа "rvalue reference to Foo". Когда вы возвращаете его, так как копия должна быть построена (в связи с типом возвращаемого значения), вы строите совершенно новое значение, которое делает bar(...)prvalue, согласно §3.10.1.5:

Prvalue ("pure" rvalue) является значением r, которое не является значением x. [Пример: результат вызова функции, тип возврата которой не является ссылкой, является значением prvalue. Значение литерала, такого как 12, 7.3e5 или true, также является значением prvalue. - конец пример]

В связи с тем, что внутри функции, foo является именующее, выражение return foo не является кандидатом на строительство переезда и the copy constructor is selected.

И да, здесь применяется RVO, предполагая, что движение не выбрано первым. В этом нет ничего особенного. В соответствии с §12.8.31:

Этот элизии от копирования/перемещения операции, называемой копией элизии, допускается в следующих случаях (которые могут быть объединены, чтобы исключить несколько копий):

  • в return в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта (кроме функции или параметра catch-clause) с тем же самым cv-неквалифицированным типом, что и возвращаемый тип функции, копия/перемещение можно опустить, построив автоматический объект непосредственно в возвращаемое значение функции

[...]


Чтобы уточнить, foo сами по себе именующему, но заявление:

return foo; 

в конечном счете результаты (от выражения bar(...)) в prvalue из-за того, что при условии, что возврат тип, выражение равнозначно:

return Foo(foo); 

, что означает, что временное значение, скопированное из foo возвращается из функции bar.


Перечитывая ответ, он по-прежнему не имеет смысла для меня. Вы говорите, из-за того, что внутри функции foo является lvalue, выражение return foo не является кандидатом на конструкцию перемещения и выбран конструктор копирования. Почему это верно в одном случае, а не в другом?

При возврате foo вы должны создать новое значение Foo (потому что вы возвращаете копию) из Lvalue ссылки foo. Это делается неявно конструктором копирования. Таким образом, return foo; эквивалентен return Foo(foo). Учитывая, что foo является lvalue, выбирается конструктор копирования (а не конструктор перемещения).

Теперь, когда у вас есть это новое временное значение (построенное из foo), само значение, которое выходит из выражения bar(...), является prvalue. Так что, когда вы делаете:

auto res = bar(...); 

Вы должны построить Foo копию из prvalue. Поскольку prvalue также является rvalue, выбирается конструктор с ссылочным параметром rvalue (move constructor).

+0

Спасибо за ответ Джеффри, но выражение foo может быть различным в разных выражениях внутри функции, и я хотел бы, в частности, понять оператор возврата. Я отредактировал вопрос, чтобы лучше отразить это. Возможно, мое предположение о том, что возвращение является значением x в предыдущем случае, неверно? Но это мотивировано первым битом в http://stackoverflow.com/a/18447976/563765 – ricab

+0

@ricab См. Edit. – Shoe

+0

* «И да, здесь применяется RVO». * Которая копия/перемещение исчезает? – dyp

Смежные вопросы