Почему требования RVO такие строгие?
Еще одно "почему должны std::move
предотвратить (безымянный) возврат-оптимизацию?"
Почему std::move предотвращает RVO? объясняет, что стандарт специально требует, чтобы объявленный тип возвращаемого значения функции соответствовал типу выражения в return
заявление. Это объясняет поведение соответствующих компиляторов; однако, это не объясняет обоснование ограничения.
Почему правила для RVO не делают исключения для случая, когда тип возвращаемого значения функции T
и тип return
выражение T&&
?
Я также знаю, что реализация таких вещей в компиляторах не бесплатна. Я предлагаю только, чтобы такое исключение было разрешено, но не обязательно.
Я также знаю, что return std::move(...)
не требуется, поскольку C++11 уже требует использования семантики перемещения, когда RVO не может быть применен. Тем не менее, почему бы не допустить явного запроса на оптимизацию вместо того, чтобы превратить его в пессимизацию?
(В сторону: почему return-value-optimization
а также rvo
теги не синонимы?)
1 ответ
auto foo() -> T&&;
auto test() -> T
{
return foo();
}
Вы говорите, что в этом случае следует разрешить применение RVO. Однако рассмотрим эту правовую реализацию foo
:
T val;
auto foo() -> T&&
{
return static_cast<T&&>(val); // because yes, it's legal
}
Мораль: только с prvalues вы точно знаете, что у вас есть временное и самое важное, вы знаете точный срок службы временного, чтобы вы могли исключить его строительство и разрушение. Но с xvalues (например, T&&
вернитесь) вы не знаете, действительно ли это связано с временным, вы не знаете, когда это значение было создано и когда оно выходит из области видимости, или даже если вы знаете, что вы не можете изменить его точку построения и разрушения, как приведенный выше пример.
Я не уверен, что полностью понимаю. Если бы RVO было разрешено применять к
test()
почему это будет хуже, чем если бы тест сделал: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()
вы не можете удалить копию просто потому, что не можете знать, к какому объекту привязана обратная ссылка. Это может быть ссылка на любой объект.