Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference in the end of lifetime rules?

https://en.cppreference.com/w/cpp/language/lifetime in Notes section has this code, reproduced here:

struct A {   int* p;   ~A() { std::cout << *p; } // if n outlives a, prints 123 }; void f() {   A a;   int n = 123; // if n does not outlive a, this is optimized out (dead store)   a.p = &n; } 

What is it trying to say in this Notes section?

From what I understand, the code is UB (or is it) as it's clear that n does not outlive a.

What does it mean by:

difference in the end of lifetime rules between non-class objects (end of storage duration) and class objects (reverse order of construction) matters

But it does not say matter how.

I am very confused by this entire section.

like image 395
PoweredByRice Avatar asked Dec 14 '18 19:12

PoweredByRice


People also ask

How can you extend the life time of an object?

The lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference (since C++11), see reference initialization for details.

How long do I have to pay spousal support in Ontario?

Support duration ranges from half to one year for each year of marriage (or cohabitation), with duration becoming indefinite after 20 years of marriage.

What is the lifetime of an object?

In object-oriented programming (OOP), the object lifetime (or life cycle) of an object is the time between an object's creation and its destruction.

What is lifetime of local object in C++?

The lifetime of a variable or object is the time period in which the variable/object has valid memory. Lifetime is also called "allocation method" or "storage duration."


2 Answers

This is an odd aspect of C++'s lifetime rules. [basic.life]/1 tells us that an object's lifetime ends:

  • if T is a class type with a non-trivial destructor ([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 ([intro.object]).

Emphasis added. int is not a "class type with a non-trivial destructor", so its lifetime only ends when the storage it occupies is released. By contrast, A is a class type with a non-trivial destructor", so its lifetime ends when the destructor gets called.

The storage for a scope is released when the scope exits, pursuant to [basic.stc.auto]/1:

The storage for [variables with automatic storage duration] lasts until the block in which they are created exits.

But automatic variables are destroyed in accord with [stmt.jump]/2:

On exit from a scope (however accomplished), objects with automatic storage duration that have been constructed in that scope are destroyed in the reverse order of their construction.

Notice that the order of destruction is specified, but the order of automatic storage release is not specified. This means that the implementation could release the storage right after each variable is destroyed, or release it all at once later, or in some arbitrary other order.

Now, the fact that it uses the singular for storage ("the storage for ... lasts") rather than talking about each variable individually may suggest that the intent is for the storage as a whole to be released at once for that scope. But there is no explicit statement of this in the standard. So as long as a variable is destroyed before its storage is released, any ordering of destruction vs. release appears to be legal.

This means it is entirely possible for the code to work, for n to outlive a. But it is unspecified whether it does work.

like image 79
Nicol Bolas Avatar answered Oct 14 '22 07:10

Nicol Bolas


This example is borrowed from Core Language Issue 2256:

Section: 6.8 [basic.life] Status: drafting Submitter: Richard Smith Date: 2016-03-30

According to 6.4 [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.

Notes from the March, 2018 meeting:

CWG agreed with the suggested direction.

The key idea is, whether the lifetime of an object ends at its destruction or at the time its memory is released may affect the semantic of a program. In the example,

  1. if the lifetime of n ends at the destruction of n, the program is undefined;

  2. if the lifetime of n ends until the memory is released, the program has defined behavior1.

Hence it requires further discussion to determine when the lifetime of an object ends.


1 This is because Core Language Issue 2115:

Section: 9.6 [stmt.jump] Status: drafting Submitter: Richard Smith Date: 2015-04-16

The relative ordering between destruction of automatic variables on exit from a block and the release of the variables' storage is not specified by the Standard: are all the destructors executed first and then the storage released, or are they interleaved?

Notes from the February, 2016 meeting:

CWG agreed that the storage should persist until all destructions are complete, although the “as-if” rule would allow for unobservable optimizations of this ordering.

The intent is that the memory release of automatic variables happens after all destructions are complete.

like image 40
xskxzr Avatar answered Oct 14 '22 07:10

xskxzr