Ответ 1
auto foo() -> T&&;
auto test() -> T
{
return foo();
}
Вы говорите, что в этом случае RVO должно быть разрешено применять. Однако рассмотрите эту юридическую реализацию foo
:
T val;
auto foo() -> T&&
{
return static_cast<T&&>(val); // because yes, it legal
}
Мораль: только с провалами, которые вы знаете наверняка, у вас есть временное и самое важное, вы знаете точное время жизни временного, чтобы вы могли избежать его строительства и разрушения. Но с xvalues (например, возврат T&&
) вы не знаете, действительно ли это связано с временным, вы не знаете, когда это значение было создано и когда оно выходит из сферы действия, или даже если вы знаете, что не можете его изменить строительство и разрушение, как показано выше.
Я не уверен, что полностью понимаю. Если RVO разрешено применять для
test()
, почему это было бы хуже, чем в случае теста:T temp = foo(); return temp;
T temp = foo(); return temp;
что позволило бы NRVO?
Это не то, что это хуже. Это просто невозможно. С вашим примером temp
является локальной переменной в функции, где вы хотите применить NRVO, т.е. test
. Таким образом, это объект, полностью "известный" в контексте test
, известно его время жизни, известна нормальная точка ctor и dtor. Поэтому вместо того, чтобы создавать temp
переменную в кадре стека test
она создается в кадре стека вызывающего. Это означает, что нет копии объекта из фрейма стека test
в стек стека вызывающего. Также, пожалуйста, посмотрите, что в этом примере foo()
совершенно не имеет значения. Это могло быть что угодно при инициализации temp
:
auto test() -> T
{
T temp = /*whatever*/;
return temp; // NRVO allowed
}
Но с return foo()
вы не можете удалить копию просто потому, что не можете знать, к какому объекту привязывается ссылка возврата. Это может быть ссылка на любой объект.