Lifetime of trivially-destructible objects
6.7.3 [basic.life]
Richard Smith

Created on 2016-03-30.00:00:00 last changed 7 months ago


Date: 2018-11-15.00:00:00

Proposed resolution (November, 2018):

  1. Change 6.7.3 [basic.life] paragraph 1 as follows:

  2. The lifetime of an object or reference is a runtime property of the object or reference. An object is said to have non-vacuous initialization if it is of a class or array type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. [Note: : Initialization by a trivial copy/move constructor is non-vacuous initialization. —end note] A variable is said to have vacuous initialization if it is default-initialized and, if it is of class type or (possibly multi-dimensional) array thereof, that class type has a trivial default constructor. The lifetime of an object of type T begins when:

    • storage with the proper alignment and size for type T is obtained, and

    • if the object has non-vacuous initialization, its initialization (if any) is complete (including vacuous initialization) (9.4 [dcl.init],

    except that if the object is a union member or subobject thereof, its lifetime only begins if that union member is the initialized member in the union (9.4.2 [dcl.init.aggr], 11.9.3 [class.base.init]), or as described in 11.5 [class.union]. The lifetime of an object o of type T ends when:

    • if T is a non-class type, the object is destroyed, or

    • if T is a class type with a non-trivial destructor (11.4.7 [class.dtor]), the destructor call starts, or

    • the storage which the object occupies is released, or is reused by an object that is not nested within o (6.7.2 [intro.object]).

  3. Change [basic.start.term] paragraphs 1 and 2 as follows:

  4. Destructors (11.4.7 [class.dtor]) for initialized Constructed objects (that is, objects whose lifetime (6.7.3 [basic.life]) has begun 9.4 [dcl.init]) with static storage duration, are destroyed and functions registered with std::atexit, are called as part of a call to std::exit (17.5 [support.start.term]). The call to std::exit is sequenced before the invocations of the destructors destructions and the registered functions. [Note: Returning from main invokes std::exit ( [basic.start.main]). —end note]

    Destructors for initialized Constructed objects with thread storage duration within a given thread are called destroyed as a result of returning from the initial function of that thread and as a result of that thread calling std::exit. The completions of the destructors for destruction of all initialized constructed objects with thread storage duration within that thread strongly happens before the initiation of the destructors of destroying any object with static storage duration.

  5. Change 8.7 [stmt.jump] paragraph 2 as follows:

  6. ...[Note: However, the program can be terminated (by calling std::exit() or std::abort() (17.5 [support.start.term]), for example) without destroying class objects with automatic storage duration. —end note]
  7. Change 8.8 [stmt.dcl] paragraph 2 as follows:

  8. It is possible to transfer into a block, but not in a way that bypasses declarations with initialization (including ones in conditions and init-statements). A program that jumps92 from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer vacuous initialization (9.4 [dcl.init]). In such a case, the variables with vacuous initialization are constructed in the order of their declaration. [Example:...
  9. Change 8.8 [stmt.dcl] paragraph 5 as follows:

  10. The destructor for a A block-scope object with static or thread storage duration will be executed destroyed if and only if it was constructed. [Note: [basic.start.term] describes the order in which block-scope objects with static and thread storage duration are destroyed. —end note]
  11. Change 9.4 [dcl.init] paragraph 21 as follows:

  12. An object whose initialization has completed is deemed to be constructed, even if the object is of non-class type or no constructor of the object's class is invoked for the initialization. [Note: Such an object might have been value-initialized or initialized by aggregate initialization (9.4.2 [dcl.init.aggr]) or by an inherited constructor (11.9.4 [class.inhctor.init]). —end note] Destroying an object of class type invokes the destructor of the class. Destroying a scalar type has no effect other than ending the lifetime of the object (6.7.3 [basic.life]). Destroying an array destroys each element in reverse subscript order.
  13. Change 11.4.7 [class.dtor] paragraph 2 as follows:

  14. A destructor is used to destroy objects of its class type. The address of a destructor...
  15. Change 14.3 [except.ctor] paragraphs 1 and 2 as follows:

  16. As control passes from the point where an exception is thrown to a handler, destructors are invoked objects with automatic storage duration are destroyed by a process, specified in this subclause, called stack unwinding.

    The destructor is invoked for each automatic object of class type Each object with automatic storage duration is destroyed if it has been constructed, but not yet destroyed, since the try block was entered. If an exception is thrown during the destruction of temporaries or local variables for a return statement (8.7.4 [stmt.return]), the destructor for the returned object (if any) is also invoked. The objects are destroyed in the reverse order of the completion of their construction. [Example:...

Date: 2018-03-15.00:00:00

Notes from the March, 2018 meeting:

CWG agreed with the suggested direction.

Date: 2019-02-15.00:00:00

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

According to 6.5 [basic.lookup] bullet 1.4, the following example has defined behavior because the lifetime of n extends until its storage is released, which is after a's destructor runs:

  void f() { 
    struct A { int *p; ~A() { *p = 0; } } a; 
    int n; 
    a.p = &n; 

It would be more consistent if the end of the lifetime of all objects, regardless of whether they have a non-trivial destructor, were treated the same.

Date User Action Args
2020-12-15 00:00:00adminsetmessages: + msg6362
2020-12-15 00:00:00adminsetstatus: drafting -> cd5
2018-04-11 00:00:00adminsetmessages: + msg6189
2018-04-11 00:00:00adminsetstatus: open -> drafting
2016-03-30 00:00:00admincreate