Какая структура функций лучше?
Посмотрите на следующий код:
class MyClass{
public:
MyClass(){}
MyClass(MyClass &&){}
MyClass(const MyClass &){}
};
MyClass f1(){
MyClass &&o=MyClass();
/*...*/
return std::move(o);//or return static_cast<MyClass &&>(o);
}
MyClass f2(){
MyClass o=MyClass();
/*...*/
return o;
}
int main(int, char **){
auto a=f1();
auto b=f2();
}
функция f2
это нормальная форма возврата объекта. NRVO может применяться, и можно избежать дополнительного вызова конструктора копирования. f1
это новая форма, которая использует ссылку rvalue. Для систем, которые не поддерживают NRVO, но поддерживают ссылку на rvalue, вызывается конструктор перемещения, а не конструктор копирования, что в большинстве случаев считается лучшим.
Проблема f1
это: есть ли компиляторы, которые поддерживают NRVO в этом случае? В конце концов, это, кажется, лучшая форма в будущем.
2 ответа
Вот как работают текущие компиляторы (магистраль MSVC10 / gcc):
Предполагая, что MyClass является подвижным
f1 : move
f2 :
worst case : move
best case : NRVO
Предполагая, что MyClass не может быть перемещен:
f1 : copy
f2 :
worst case : copy
best case : NRVO
Поэтому, даже если компиляторы становятся лучше и начинают выполнять NRVO для таких функций, как f1, зачем усложнять код, когда классические функции C++03 уже оптимальны?
есть ли поддержка компилятора NRVO в этом случае?
Определить "поддержку компилятора"?
Какие f1
делает полностью разрушить способность компилятора оптимизировать копию MyClass
, Давайте посмотрим на f1
в деталях
MyClass &&o=MyClass();
Это создает временную, а не переменную стека. Затем этот временный объект связан со ссылкой на r-значение, называемой o
, который продлевает время жизни временного до конца функции.
return std::move(o); //or return static_cast<MyClass &&>(o);
Это возвращает ссылку на r-значение привязанной к стеку ссылки на r-значение для временного объекта. И поскольку вы возвращаете значение, а не ссылку, компилятор должен создать из него временное значение.
Копирование / перемещение временного в a
будет исключен. Но вы все равно создали два временных (исходное и возвращаемое значение).
Так f1
делает следующее:
create temporary
copy/move from temporary to return value
elide copy/move from return value to `a`.
f2
делает:
create stack variable
elide copy/move from stack variable to `b`.
Если NVRO не существует, у вас есть:
create stack variable
copy/move from stack variable to return value
elide copy/move from stack variable to `b`.
Так, f2
в худшем случае равен f1
, И более чем вероятно, лучше.
Пожалуйста, перестаньте пытаться перехитрить компилятор. Просто позвольте копии elision делать свою работу.