Будет ли компилятор C ++ оптимизировать код возврата по значению?

Предположим, я использую 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 );

4

Решение

Современные компиляторы часто оптимизируют этот вид операций. Увидеть оптимизация возвращаемого значения

7

Другие решения

Это оптимизация, которая по определению означает, что компилятору и каждому компилятору не обязательно решать, что делать. Как вы можете узнать наверняка? Проверьте разборку сгенерированного кода!

Тем не менее, большинство компиляторов должны выполнять эту оптимизацию (оптимизацию возвращаемого значения [RVO]), поскольку в этом случае это сделать относительно легко (без многократных возвратов, это неназванный временный код, поэтому у вас нет псевдонимов и т. Д.).

2

Мне кажется, что приведенный тестовый пример достаточно прост для РВО применять.

1

Я сомневаюсь, что временное оптимизировано. Вы можете проверить это, поместив оператор print в конструктор и конструктор копирования и посмотреть, что печатается при различных настройках компилятора.

0

Вы можете проверить это самостоятельно, потому что рассматриваемая оптимизация имеет заметные различия!

Большинство форм оптимизации в 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 (оптимизация именованных возвращаемых значений) относительно хрупки, и если вы полагаетесь на них, вы оба должны понимать, как они работают, что их нарушает, и любые причуды, которые ваш конкретный компилятор имеет в своем реализация (если есть).

0
По вопросам рекламы [email protected]