Search code examples
c++standardsc++17

Copy Elision for Returned Temporaries


I'm trying to understand lifetime guaranteed by the C++17 Standard, in particular for guaranteed copy elisions. Let's start with an example

std::string make_tmp();

std::string foo() {
   return std::string{make_tmp().c_str()};
}

My undestanding of what's happening: make_tmp creates a temporary string we will call t; foo returns a (needlessly created) temporary (copy of t's c_str). The standard (even pre C++17) guarantees the lifetime of t to be the time until the full return expressions has been evaluated. Thus it is safe so create the temporary copy of t (to be returned).

Now copy elisions kicks in; more specifically, the second bullet in the first C++17 block:

In a function call, if the operand of a return statement is a prvalue and the return type of the function is the same as the type of that prvalue.

Consequently the temporary copy will not even be created at all.

Follow-up Questions:

  1. Does the returned temporary copy still imply a sufficiently extended lifetime of t -- even though it is guaranteed to be elided?

  2. Consider the variant of foo given below. I'm assuming, copy elision is no longer required (but rather highly likely). If the copy will not be elided, the standard's got us covered (by the arguments above). In case the copy is elided, does the standard still guarantee a sufficient lifetime of t despite the type of the returned-expression being different from foo's return type?

foo-Variant:

std::string foo() {
   return make_tmp().c_str();
}

I'd like to understand the guarantees purely implied by the standard. Please note, that I'm aware of the fact that both foo versions "work" (ie. there are no dangling pointers involved even when testing with custom classes under various compilers).


Solution

  • This answer directly answers the lifetime issues asked in OP (and you can see it has nothing to do with copy elision). If you are not familiar with the whole story happened during the execution of the return statement, you can refer to Barry's answer.


    Yes, the temporary is guaranteed to persist during the copy-initialization of the returned object per [stmt.return]/2:

    The copy-initialization of the result of the call is sequenced before the destruction of temporaries at the end of the full-expression established by the operand of the return statement, which, in turn, is sequenced before the destruction of local variables ([stmt.jump]) of the block enclosing the return statement.