Search code examples
c++c++11object-lifetimetemporary-objects

What's the rationale of the exceptions of temporary object lifetime expansion when bound to a reference?


In 12.2 of C++11 standard:

The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:

  1. A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.

  2. A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.

  3. The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.

  4. A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the full-expression containing the new-initializer.

And there is an example of the last case in the standard:

struct S {
  int mi; 
  const std::pair<int,int>& mp;
}; 
S a { 1,{2,3} };  // No problem.
S* p = new S{ 1, {2,3} };  // Creates dangling reference

To me, 2. and 3. make sense and easy to agree. But what's the reason bebind 1. and 4.? The example looks just evil to me.


Solution

  • As with many things in C and C++, I think this boils down to what can be reasonably (and efficiently) implemented.

    Temporaries are generally allocated on the stack, and code to call their constructors and destructors are emitted into the function itself. So if we expand your first example into what the compiler is actually doing, it would look something like:

      struct S {
        int mi;
        const std::pair<int,int>& mp;
      };
    
      // Case 1:
      std::pair<int,int> tmp{ 2, 3 };
      S a { 1, tmp };
    

    The compiler can easily extend the life of the tmp temporary long enough to keep "S" valid because we know that "S" will be destroyed before the end of the function.

    But this doesn't work in the "new S" case:

      struct S {
        int mi;
        const std::pair<int,int>& mp;
      };
    
      // Case 2:
      std::pair<int,int> tmp{ 2, 3 };
      // Whoops, this heap object will outlive the stack-allocated
      // temporary!
      S* p = new S{ 1, tmp };
    

    To avoid the dangling reference, we would need to allocate the temporary on the heap instead of the stack, something like:

       // Case 2a -- compiler tries to be clever?
       // Note that the compiler won't actually do this.
       std::pair<int,int> tmp = new std::pair<int,int>{ 2, 3 };
       S* p = new S{ 1, tmp };
    

    But then a corresponding delete p would need to free this heap memory! This is quite contrary to the behavior of references, and would break anything that uses normal reference semantics:

      // No way to implement this that satisfies case 2a but doesn't
      // break normal reference semantics.
      delete p;
    

    So the answer to your question is: the rules are defined that way because it sort of the only practical solution given C++'s semantics around the stack, heap, and object lifetimes.

    WARNING: @Potatoswatter notes below that this doesn't seem to be implemented consistently across C++ compilers, and therefore is non-portable at best for now. See his example for how Clang doesn't do what the standard seems to mandate here. He also says that the situation "may be more dire than that" -- I don't know exactly what this means, but it appears that in practice this case in C++ has some uncertainty surrounding it.