Title
Lifetime extension of references vs exceptions
Status
cd5
Section
6.7.7 [class.temporary]
Submitter
Hubert Tong

Created on 2016-04-07.00:00:00 last changed 7 months ago

Messages

Date: 2019-01-15.00:00:00

Proposed resolution (January, 2019):

Change 14.3 [except.ctor] paragraph 3 as follows:

If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object's direct subobjects and, for a complete object, virtual base class subobjects, whose initialization has completed (9.4 [dcl.init]) and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. [Note: If such an object has a reference member that extends the lifetime of a temporary object, this ends the lifetime of the reference member, so the lifetime of the temporary object is effectively not extended.—end note] The subobjects are destroyed in the reverse order of the completion of their construction. Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.
Date: 2016-12-15.00:00:00

Notes from the December, 2016 teleconference:

The consensus was that the temporaries should be destroyed immediately if an exception occurs. 14.3 [except.ctor] paragraph 3 should be extended to apply to static initialization, so that even if a temporary is lifetime-extended (because it has static storage duration), it will be destroyed in case of an exception.

Date: 2019-02-15.00:00:00

[Accepted as a DR at the February, 2019 meeting.]

There is implementation divergence on the following example:

  #include <stdio.h> 
  struct A { 
    bool live; 
    A() : live(true) { 
      static int cnt; 
      if (cnt++ == 1) throw 0; 
    } 
    ~A() { 
      fprintf(stderr, "live: %d\n", live); 
      live = false; 
    } 
  }; 
  struct AA { A &&a0, &&a1; }; 
  void doit() { 
    static AA aa = { A(), A() }; 
  } 
  int main(void) { 
    try { 
      doit(); 
    } 
    catch (...) { 
      fprintf(stderr, "in catch\n"); 
      doit(); 
    } 
  }

Some implementations produce

  in catch
  live: 1
  live: 1
  live: 0

While others produce

  live: 1
  in catch
  live: 1
  live: 1

With regard to the reference to which the first-constructed object of type A is bound, at what point should its lifetime end? Perhaps it should be specified that the lifetime of the temporary is only extended if said initialization does not exit via an exception (which means that calling ::std::exit(0) as opposed to throwing would not result in a call to ~A()).

See also issue 1634.

History
Date User Action Args
2020-12-15 00:00:00adminsetmessages: + msg6366
2020-12-15 00:00:00adminsetstatus: drafting -> cd5
2018-04-11 00:00:00adminsetstatus: open -> drafting
2017-02-06 00:00:00adminsetmessages: + msg5817
2016-04-07 00:00:00admincreate