Pergunta

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

NOTE: Function func() definition and its usage 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 );
Foi útil?

Solução

Modern compilers will often optimize this kind of operation. See return value optimization

Outras dicas

It's an optimization which pretty much by definition means it's optional for the compiler and up to each aprticular compiler to decide what to do. How can you find out for sure? Check the disassembly of the generated code!

That said most compilers should do this optimization (return value optimization [RVO]) as it's relatively easy to do in this case (no multiple returns, it's an unnamed temporary so you don't have aliasing, etc).

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

I would doubt the temporary is optimised out. You could test it by putting a print statement in the constructor and copy constructor and see what's printed under different compiler settings.

You can test this yourself, because the optimization in question has observable differences!

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

In this case, add the following to 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;
  }
}

and run your code. With zero optimization you'll get copies and/or moves.

With any non-trivial level of optimization, the prints will disappear, because RVO (and, in related cases, NRVO) will run, eliminating the copies. (If your compiler isn't C++11, remove the move constructors above -- the optimization in C++03 was still allowed)

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

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top