Home > other >  Does a constant reference member variable in an anonymous struct extend the lifetime of a temporary?
Does a constant reference member variable in an anonymous struct extend the lifetime of a temporary?

Time:01-20

Consider the following code:

struct Temp{ int i = 0; };

Temp GetTemp() { return Temp{}; }

int main()
{
    for (struct{Temp const & t; int counter;} v = {GetTemp(), 0};
        v.counter < 10; 
          v.counter) 
    {
      // Is v.t guaranteed to not be a dangling reference?
      std::cout << v.t.i << std::endl;
    }
}

So GetTemp() returns a temporary object, which then gets assigned to a constant reference variable. However, that constant reference variable is a member of an anonymous local struct. Question: Does the C standard guarantee that the lifetime of that temporary gets extended till after the loop terminates?

Considering this question, I would have expected the answer to be no, i.e. that I get a dangling reference in the loop body. However, gcc and clang seem to extend the lifetime (see example on godbolt), and even do not complain with -fsanitize=undefined, which surprised me.

CodePudding user response:

For braced aggregate initialization as in your example, lifetime extension has been guaranteed since C 98 (irrespective of the linkage/visibility properties of the class). This is intuitive, since the reference is directly bound to the temporary, and not via some intermediate ctor parameter, as in the question you've linked. For legalese, see [class.temporary] in the C 14 FD that outlines the lifetime extension contexts.

See also the note here which differentiates braced and parenthetical initialization since C 20:

[Note 7: By contrast with direct-list-initialization, narrowing conversions ([dcl.init.list]) are permitted, designators are not permitted, a temporary object bound to a reference does not have its lifetime extended ([class.temporary]), and there is no brace elision.
[Example 3:

struct A {
    int a;
    int&& r;
};

int f();
int n = 10;

A a1{1, f()};                   // OK, lifetime is extended 
A a2(1, f());                   // well-formed, but dangling reference

CodePudding user response:

Does a constant reference member variable in an anonymous struct extend the lifetime of a temporary?

Yes, the chain of const references is unbroken. There's is only v and v is alive until the end of the for loop and the lifetime of the referenced Temp is therefore extended until then. The fact that the struct is anonymous has no impact.

class.temporary/4

There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default constructor is called to initialize an element of an array. If the constructor has one or more default arguments, the destruction of every temporary created in a default argument is sequenced before the construction of the next array element, if any.

class.temporary/5

The second context is when a reference is bound to a temporary. 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:

  • (5.1) A temporary bound to a reference member in a constructor's ctor-initializer ([class.base.init]) persists until the constructor exits.
  • (5.2) A temporary bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.
  • (5.3) The lifetime of a temporary bound to the returned value in a function return statement ([stmt.return]) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
  • (5.4) A temporary bound to a reference in a new-initializer ([expr.new]) persists until the completion of the full-expression containing the new-initializer.

None of the exceptions to the lifetime extension applies to your case.

  • Related