Тема RVO была затронута zorgg'ом в одном из комментариев, я решила написать про нее подробнее.
RVO расшифровывается как return value optimization, оптимизация возвращаемого значения. В случае такого вот кода:
std::string foo()
{
return std::string('a',1000000);
}
компилятор может не создавать временный объект и сразу же создать строку в возвращаемом объекте. Это старая оптимизация, Скотт Мейерс рассказывает о ней в Правиле 20
второй своей "эффективной" книги. Он особенно напирает на то, что эта оптимизация удобна при переопределении операторов.
Кроме RVO есть еще NRVO, она поновее, ее сложнее было реализовать в компиляторах. NRVO расшифровывается как named return value optimization, оптимизация именованного возращаемого значения. NRVO применяется в случаях чуть отличных от предыдущего.
std::string foo()
{
std::string a('a',1000000);
return a;
}
Тут Стандарт позволяет компилятору не создавать локальный объект, хотя не требует такого поведения (12.8/15).
При NRVO не просто не происходит вызова конструктора копирования, здесь даже не будет побитового присваивания.
Про NRVO есть хорошая статья на MSDN, где
рассказывается как NRVO работает, когда не работает, почему при применении NRVO у компилятора могут появиться проблемы. В статье рассматривается компилятор Visual C++ 2005, но все компиляторы будут делать примерно то же самое. Хороший пример оттуда.
Есть такая функция:
A MyMethod (B &var)
{
A retVal;
retVal.member = var.value + bar(var);
return retVal;
}
Которая вызывается где-то вот таким образом
valA = MyMethod(valB);
Псевдокод, который подробнее рассказывает о том, что будет происходить в функции без NRVO.
A MyMethod (A &_hiddenArg, B &var)
{
A retVal;
retVal.A::A(); // constructor for retVal
retVal.member = var.value + bar(var);
_hiddenArg.A::A(retVal); // the copy constructor for A
return;
retVal.A::~A(); // destructor for retVal
}
А вот с NRVO все происходит несколько быстрее, потому что локальная переменная retVal вообще выбрасывается
A MyMethod(A &_hiddenArg, B &var)
{
_hiddenArg.A::A();
_hiddenArg.member = var.value + bar(var);
Return
}
NRVO можно применить не всегда, в этой MSDN'овской статье рассматриваются случаи, когда при ветвлениях в функции возвращаются разные значения, еще возможны проблемы при работе с исключениями и с ассемблерными вставками.
Чтобы проверить, есть ли в компиляторе NRVO, можно воспользоваться вот таким
кусочком кода из блога Стенли Липпмана.
#include <iostream>
using namespace std;
class foo {
public:
foo() { cout <<"foo::foo()\n"; }
foo( const foo& ){ cout << "foo::foo( const foo& )\n"; }
~foo(){ cout << "foo::~foo()\n"; }
};
foo bar(){ foo local_foo; return local_foo; }
int main()
{
foo f = bar();
}
Если при запуске выдалось вот такое, то NRVO не отработало.
foo::foo()
foo::foo( const foo& )
foo::~foo()
foo::~foo()
Возможно, надо поиграть с найстройками оптимизации, а, возможно, компилятор вовсе не умеет NRVO.
Важный момент, который всегда упоминается при обсуждении этих оптимизаций. При их использовании могут возникнуть проблемы, если определенный вами конструктор копирования делает не только копирование.
Ссылки по теме:
Named Return Value Optimization in Visual C++ 2005
comp.lang.c++.moderated - RVO
comp.lang.c++.moderated - Return value optimization
comp.lang.c++.moderated - Return Value Optimization
Несмотря на то, что последние две ссылки называются одинаково, это разные обсуждения