Предположим, я использую Visual Studio или современный GCC с -O2. Будет ли компилятор создавать S
внутри func()
а затем скопировать его в my_result
или это создаст my_result
с конструктором (5, 6, 5 + 6)
без создания временного S
?
НОТА: функция func()
определение и его использование находятся в отдельных файлах .obj!
struct S
{
S(int _x, int _y, int _z) : x(_x), y(_y), z(_z) { }
int x, y, z;
};
S func(int a, int b)
{
return S(a, b, a + b);
}/// USAGE ///
S my_result = func( 5, 6 );
Современные компиляторы часто оптимизируют этот вид операций. Увидеть оптимизация возвращаемого значения
Это оптимизация, которая по определению означает, что компилятору и каждому компилятору не обязательно решать, что делать. Как вы можете узнать наверняка? Проверьте разборку сгенерированного кода!
Тем не менее, большинство компиляторов должны выполнять эту оптимизацию (оптимизацию возвращаемого значения [RVO]), поскольку в этом случае это сделать относительно легко (без многократных возвратов, это неназванный временный код, поэтому у вас нет псевдонимов и т. Д.).
Мне кажется, что приведенный тестовый пример достаточно прост для РВО применять.
Я сомневаюсь, что временное оптимизировано. Вы можете проверить это, поместив оператор print в конструктор и конструктор копирования и посмотреть, что печатается при различных настройках компилятора.
Вы можете проверить это самостоятельно, потому что рассматриваемая оптимизация имеет заметные различия!
Большинство форм оптимизации в C ++ следуют as-if
правило, которое затрудняет их обнаружение. Однако в некоторых случаях допускается исключение (пропуск) конструктора копирования и перемещения, даже если различие приводит к наблюдаемым изменениям поведения.
В этом случае добавьте следующее к S:
struct S {
// ...
S( S const& o ):x(o.x), y(o.y), z(o.z) {
std::cout << "copy ctor!\n";
}
S& operator=( S const& o ) {
x=o.x;
y=o.y;
z=o.z;
std::cout << "copy assign!\n";
return *this;
}
S( S && o ):x(std::move(o.x)), y(std::move(o.y)), z(std::move(o.z)) {
std::cout << "move ctor!\n";
}
S& operator=( S const& o ) {
std::tie( x,y,z ) = std::tie( std::move(o.x),std::move(o.y),std::move(o.z) );
std::cout << "move assign!\n";
return *this;
}
}
и запустите свой код. С нулевой оптимизацией вы получите копии и / или ходы.
При любом нетривиальном уровне оптимизации отпечатки исчезнут, потому что будет выполняться RVO (и, в соответствующих случаях, NRVO), удаляя копии. (Если ваш компилятор не C ++ 11, удалите конструкторы перемещения выше — оптимизация в C ++ 03 все еще была разрешена)
В C ++ 11 вы можете явно создать возвращаемое значение вместо того, чтобы полагаться на NRVO / RVO через return {stuff}
синтаксис.
Обратите внимание, что RVO (оптимизация возвращаемых значений) и NRVO (оптимизация именованных возвращаемых значений) относительно хрупки, и если вы полагаетесь на них, вы оба должны понимать, как они работают, что их нарушает, и любые причуды, которые ваш конкретный компилятор имеет в своем реализация (если есть).