Will the C ++ compiler optimize return code to value?

Suppose I use Visual Studio or modern GCC with -O2. Will the compiler create S inside func() and then copy it to my_result or create my_result with constructor (5, 6, 5 + 6) without creating a temporary S ?

NOTE: The definition of the func() function and its use are in separate .obj files!

 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
source share
5 answers

Modern compilers often optimize this kind of work. See optimizing return value

+7
source

This is an optimization, which pretty much by definition means that it is optional for the compiler and up to each applicative compiler to decide what to do. How can you find out for sure? Check out the disassembly of the generated code!

However, most compilers should do this optimization (return value optimization [RVO]), since in this case it is relatively easy to do this (no multiple returns, this is an unnamed temporary, so that you do not have aliases, etc.).

+2
source

It seems to me that the provided test case is simple enough for RVO to use.

+1
source

I would doubt that temporary is optimized. You can check it by placing the print statement in the copy constructor and constructor and see what is printed in different compiler settings.

0
source

You can verify this yourself, because the optimization in question has observed differences!

Most forms of optimization in C ++ follow the as-if rule, which makes them difficult to detect. However, in some cases it is allowed to return (copy) the copy and move the constructor, even if the difference leads to observable changes in behavior.

In this case, add the following to S:

 struct S { // ... S( S const& o ):x(ox), y(oy), z(oz) { std::cout << "copy ctor!\n"; } S& operator=( S const& o ) { x=ox; y=oy; z=oz; std::cout << "copy assign!\n"; return *this; } S( S && o ):x(std::move(ox)), y(std::move(oy)), z(std::move(oz)) { std::cout << "move ctor!\n"; } S& operator=( S const& o ) { std::tie( x,y,z ) = std::tie( std::move(ox),std::move(oy),std::move(oz) ); std::cout << "move assign!\n"; return *this; } } 

and run your code. With zero optimization, you will get copies and / or relocation.

At any non-trivial level of optimization, the fingerprints will disappear because the RVO (and, as appropriate, NRVO) will work, excluding the copies. (If your compiler is not C ++ 11, remove the move constructors above - optimization in C ++ 03 is still allowed)

In C ++ 11, you can explicitly build the return value instead of relying on NRVO / RVO using the return {stuff} syntax.

Note that RVO (return value optimization) and NRVO (called return value optimization) are relatively fragile, and if you rely on them, you both need to understand how they work, which makes them break, and any quirks of your particular compiler have in its implementation (if any).

0
source

Source: https://habr.com/ru/post/1500314/


All Articles