c++copy-constructorc++17object-lifetimecopy-elision

Does guaranteed copy elision work with function parameters?


If I understood correctly, starting from C++17, this code now requires that no copy will be done:

Foo myfunc(void) {
    return Foo();
}

auto foo = myfunc(); // no copy

Is it also true for function arguments? Will copies be optimized away in the following code?

Foo myfunc(Foo foo) {
    return foo;
}

auto foo = myfunc(Foo()); // will there be copies?

Solution

  • In C++17, prvalues ("anonymous temporaries") are no longer objects. Instead, they are instructions on how to construct an object.

    They can instantiate a temporary from their construction instructions, but as there is no object there, there is no copy/move construction to elide.

    Foo myfunc(Foo foo) {
      return foo;
    }
    

    So here, the function argument foo is moved into the prvalue return value of myfunc. You can think of this conceptually as "myfunc returns instructions on how to make a Foo". If those instructions are "not used" by your program, a temporary is automatically instantiated and uses those instructions.

    (The instructions, by the way, include time travel provisions; the time-point of construction remains inside the function itself!)

    auto foo = myfunc(Foo());
    

    So here, Foo() is a prvalue. It says "construct a Foo using the () constructor". It is then used to construct the argument of myfunc. No elision occurs, no copy constructor or move constructor is called, just ().

    Stuff then happens inside myfunc.

    myfunc returns a prvalue of type Foo. This prvalue (aka construction instructions) is used to construct the local variable auto foo.

    So what happens here is that a Foo is constructed via (), then moved into auto foo.

    Elision of function arguments into return values is not supported in C++14 nor C++17 as far as I know (I could be wrong, I do not have chapter and verse of the standard here). However, they are implicitly moved when used in a return func_arg; context.