Lifetime extension for aggregate initialization
11.9.3 [class.base.init]
Ed Catmur

Created on 2022-12-18.00:00:00 last changed 3 weeks ago


Date: 2023-01-07.20:39:02

CWG 2023-01-06

CWG is soliciting the design guidance of EWG to resolve this issue:

  1. Is #1 in the initial example intended to be ill-formed? If not, is the temporary created in the default member initializer for a intended to be lifetime-extended?
  2. Does the specification treatment of #2 (well-formed; lifetime extension of the A temporary) reflect the design intent?
  3. Is #3 intended to be ill-formed? If not, is the temporary created in the default member initializer for a intended to be lifetime-extended?

Date: 2023-01-07.20:39:02

Issue 1815 presented the following example:

  struct A {};
  struct B { A&& a = A{}; };
  B b1;       // #1
  B b2{A{}};  // #2
  B b3{};     // #3

CWG intended to make #3 behave like #2, i.e. the temporary created in the brace-or-equal initializer is lifetime-extended. Issue 1696 was adopted, ostensibly resolving issue 1815, but making #3 (and #1) ill-formed per 11.9.3 [class.base.init] paragraph 11:

A temporary expression bound to a reference member from a default member initializer is ill-formed. [ Example:
  struct A {
    A() = default;          // OK
    A(int v) : v(v) { }     // OK
    const int& v = 42;      // OK
  A a1;     // error: ill-formed binding of temporary to reference
  A a2(1);  // OK, unfortunately
-- end example]

However, the example does not consider aggregate initialization, and the specified resolution for issue 1696 is contrary to the stated intent in issue 1815.

There is considerable implementation variance: #1 is rejected by clang, but accepted by gcc and EDG (with early destruction of the temporary); #2 is uniformly accepted and the lifetime of the temporary is extended; #3 is uniformly accepted, but only gcc, clang, and MSVC extend the lifetime of the temporary, whereas EDG does not.

Note that 9.4.1 [dcl.init.general] paragraph 7 specifies that default-initialization of aggregates is as-if the initializer () is used:

To default-initialize an object of type T means:
  • If T is a (possibly cv-qualified) class type (Clause 11 [class]), constructors are considered. The applicable constructors are enumerated ( [over.match.ctor]), and the best one for the initializer () is chosen through overload resolution (12.2 [over.match]). The constructor thus selected is called, with an empty argument list, to initialize the object.
  • If T is an array type, each element is default-initialized.
  • Otherwise, no initialization is performed.

Such treatment causes early destruction of temporaries per 6.7.7 [class.temporary] bullet 6.10:

The exceptions to this lifetime rule are:
  • ...
  • A temporary object bound to a reference element of an aggregate of class type initialized from a parenthesized expression-list (9.4 [dcl.init]) persists until the completion of the full-expression containing the expression-list.
Date User Action Args
2023-01-07 20:39:02adminsetmessages: + msg7126
2022-12-18 00:00:00admincreate