Files
2025-10-25 03:02:53 +03:00

335 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[basic.life]
# 6 Basics [[basic]](./#basic)
## 6.8 Memory and objects [[basic.memobj]](basic.memobj#basic.life)
### 6.8.4 Lifetime [basic.life]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L3821)
In this subclause, “before” and “after” refer to the “happens before”
relation ([[intro.multithread]](intro.multithread "6.10.2Multi-threaded executions and data races"))[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L3825)
The [*lifetime*](#def:lifetime "6.8.4Lifetime[basic.life]") of an object or reference is a runtime property of the
object or reference[.](#2.sentence-1)
A variable is said to have [*vacuous initialization*](#def:initialization,vacuous "6.8.4Lifetime[basic.life]") if it is default-initialized, no other initialization is performed, and,
if it is of class type or a (possibly multidimensional) array thereof,
a trivial constructor of that class type is selected for
the default-initialization[.](#2.sentence-2)
The lifetime of an object of type T begins when:
- [(2.1)](#2.1)
storage with the proper alignment and size
for type T is obtained, and
- [(2.2)](#2.2)
its initialization (if any) is complete
(including vacuous initialization) ([[dcl.init]](dcl.init "9.5Initializers")),
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 ([[dcl.init.aggr]](dcl.init.aggr "9.5.2Aggregates"), [[class.base.init]](class.base.init "11.9.3Initializing bases and members")),
or as described in[[class.union]](class.union "11.5Unions"), [[class.copy.ctor]](class.copy.ctor "11.4.5.3Copy/move constructors"), and [[class.copy.assign]](class.copy.assign "11.4.6Copy/move assignment operator"),
and except as described in [[allocator.members]](allocator.members "20.2.10.2Members")[.](#2.sentence-3)
The lifetime of an object *o* of type T ends when:
- [(2.3)](#2.3)
if T is a non-class type, the object is destroyed, or
- [(2.4)](#2.4)
if T is a class type, the destructor call starts, or
- [(2.5)](#2.5)
the storage which the object occupies is released,
or is reused by an object that is not nested within *o* ([[intro.object]](intro.object "6.8.2Object model"))[.](#2.sentence-4)
When evaluating a [*new-expression*](expr.new#nt:new-expression "7.6.2.8New[expr.new]"),
storage is considered reused after it is returned from the allocation function,
but before the evaluation of the [*new-initializer*](expr.new#nt:new-initializer "7.6.2.8New[expr.new]") ([[expr.new]](expr.new "7.6.2.8New"))[.](#2.sentence-5)
[*Example [1](#example-1)*: struct S {int m;};
void f() { S x{1}; new(&x) S(x.m); // undefined behavior} — *end example*]
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L3870)
The lifetime of a reference begins when its initialization is complete[.](#3.sentence-1)
The lifetime of a reference ends as if it were a scalar object requiring storage[.](#3.sentence-2)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L3875)
[*Note [1](#note-1)*:
[[class.base.init]](class.base.init "11.9.3Initializing bases and members") describes the lifetime of base and member subobjects[.](#4.sentence-1)
— *end note*]
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L3881)
The properties ascribed to objects and references throughout this document
apply for a given object or reference only during its lifetime[.](#5.sentence-1)
[*Note [2](#note-2)*:
In particular, before the lifetime of an object starts and after its
lifetime ends there are significant restrictions on the use of the
object, as described below, in [[class.base.init]](class.base.init "11.9.3Initializing bases and members"), and
in [[class.cdtor]](class.cdtor "11.9.5Construction and destruction")[.](#5.sentence-2)
Also, the behavior of an object under construction
and destruction can differ from the behavior of an object whose
lifetime has started and not ended[.](#5.sentence-3)
[[class.base.init]](class.base.init "11.9.3Initializing bases and members") and [[class.cdtor]](class.cdtor "11.9.5Construction and destruction") describe the behavior of an object during its periods
of construction and destruction[.](#5.sentence-4)
— *end note*]
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L3895)
A program may end the lifetime of an object of class type without invoking the
destructor, by reusing or releasing the storage as described above[.](#6.sentence-1)
[*Note [3](#note-3)*:
A [*delete-expression*](expr.delete#nt:delete-expression "7.6.2.9Delete[expr.delete]") ([[expr.delete]](expr.delete "7.6.2.9Delete")) invokes the destructor
prior to releasing the storage[.](#6.sentence-2)
— *end note*]
In this case, the destructor is not implicitly invoked[.](#6.sentence-3)
[*Note [4](#note-4)*:
The correct behavior of a program often depends on
the destructor being invoked for each object of class type[.](#6.sentence-4)
— *end note*]
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L3908)
Before the lifetime of an object has started but after the storage which
the object will occupy has been allocated[21](#footnote-21 "For example, before the dynamic initialization of an object with static storage duration ([basic.start.dynamic]).") or after the lifetime of an object has ended and before the storage
which the object occupied is reused or released, any pointer that represents the address of
the storage location where the object will be or was located may be
used but only in limited ways[.](#7.sentence-1)
For an object under construction or destruction, see [[class.cdtor]](class.cdtor "11.9.5Construction and destruction")[.](#7.sentence-2)
Otherwise, such
a pointer refers to allocated
storage ([[basic.stc.dynamic.allocation]](basic.stc.dynamic.allocation "6.8.6.5.2Allocation functions")), and using the pointer as
if the pointer were of type void* is
well-defined[.](#7.sentence-3)
Indirection through such a pointer is permitted but the resulting lvalue may only be used in
limited ways, as described below[.](#7.sentence-4)
The
program has undefined behavior if
- [(7.1)](#7.1)
the pointer is used as the operand of a [*delete-expression*](expr.delete#nt:delete-expression "7.6.2.9Delete[expr.delete]"),
- [(7.2)](#7.2)
the pointer is used to access a non-static data member or call a
non-static member function of the object, or
- [(7.3)](#7.3)
the pointer is implicitly converted ([[conv.ptr]](conv.ptr "7.3.12Pointer conversions")) to a pointer
to a virtual base class, or
- [(7.4)](#7.4)
the pointer is used as the operand of a static_cast ([[expr.static.cast]](expr.static.cast "7.6.1.9Static cast")), except when the conversion
is to pointer to cv void, or to pointer to cv void and subsequently to pointer to cv char, cv unsigned char, or cv std::byte ([[cstddef.syn]](cstddef.syn "17.2.1Header <cstddef> synopsis")), or
- [(7.5)](#7.5)
the pointer is used as the operand of a dynamic_cast ([[expr.dynamic.cast]](expr.dynamic.cast "7.6.1.7Dynamic cast"))[.](#7.sentence-5)
[*Example [2](#example-2)*: #include <cstdlib>struct B {virtual void f(); void mutate(); virtual ~B();};
struct D1 : B { void f(); };struct D2 : B { void f(); };
void B::mutate() {new (this) D2; // reuses storage --- ends the lifetime of *this f(); // undefined behavior... = this; // OK, this points to valid memory}void g() {void* p = std::malloc(sizeof(D1) + sizeof(D2));
B* pb = new (p) D1;
pb->mutate(); *pb; // OK, pb points to valid memoryvoid* q = pb; // OK, pb points to valid memory pb->f(); // undefined behavior: lifetime of *pb has ended} — *end example*]
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L3978)
Similarly, before the lifetime of an object has started but after the
storage which the object will occupy has been allocated or after the
lifetime of an object has ended and before the storage which the object
occupied is reused or released, any glvalue that refers to the original
object may be used but only in limited ways[.](#8.sentence-1)
For an object under construction or destruction, see [[class.cdtor]](class.cdtor "11.9.5Construction and destruction")[.](#8.sentence-2)
Otherwise, such
a glvalue refers to
allocated storage ([[basic.stc.dynamic.allocation]](basic.stc.dynamic.allocation "6.8.6.5.2Allocation functions")), and using the
properties of the glvalue that do not depend on its value is
well-defined[.](#8.sentence-3)
The program has undefined behavior if
- [(8.1)](#8.1)
the glvalue is used to access the object, or
- [(8.2)](#8.2)
the glvalue is used to call a non-static member function of the object, or
- [(8.3)](#8.3)
the glvalue is bound to a reference to a virtual base class ([[dcl.init.ref]](dcl.init.ref "9.5.4References")), or
- [(8.4)](#8.4)
the glvalue is used as the operand of adynamic_cast ([[expr.dynamic.cast]](expr.dynamic.cast "7.6.1.7Dynamic cast")) or as the operand oftypeid[.](#8.sentence-4)
[*Note [5](#note-5)*:
Therefore, undefined behavior results
if an object that is being constructed in one thread is referenced from another
thread without adequate synchronization[.](#8.sentence-5)
— *end note*]
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4005)
An object o1 is [*transparently replaceable*](#def:transparently_replaceable "6.8.4Lifetime[basic.life]") by an object o2 if
- [(9.1)](#9.1)
the storage that o2 occupies exactly overlays
the storage that o1 occupied, and
- [(9.2)](#9.2)
o1 and o2 are of the same type
(ignoring the top-level cv-qualifiers), and
- [(9.3)](#9.3)
o1 is not a const, complete object, and
- [(9.4)](#9.4)
neither o1 nor o2 is a potentially-overlapping subobject ([[intro.object]](intro.object "6.8.2Object model")), and
- [(9.5)](#9.5)
either o1 and o2 are both complete objects, oro1 and o2 are direct subobjects of objects p1 and p2, respectively,
and p1 is transparently replaceable by p2[.](#9.sentence-1)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4024)
After the lifetime of an object has ended and before the storage which the
object occupied is reused or released, if a new object is created at the
storage location which the original object occupied and the original object was
transparently replaceable by the new object, a pointer that pointed to the
original object, a reference that referred to the original object, or the name
of the original object will automatically refer to the new object and, once the
lifetime of the new object has started, can be used to manipulate the new
object[.](#10.sentence-1)
[*Example [3](#example-3)*: struct C {int i; void f(); const C& operator=( const C& );};
const C& C::operator=( const C& other) {if ( this != &other ) {this->~C(); // lifetime of *this endsnew (this) C(other); // new object of type C created f(); // well-defined}return *this;} C c1;
C c2;
c1 = c2; // well-defined c1.f(); // well-defined; c1 refers to a new object of type C — *end example*]
[*Note [6](#note-6)*:
If these conditions are not met,
a pointer to the new object can be obtained from
a pointer that represents the address of its storage
by calling std::launder ([[ptr.launder]](ptr.launder "17.6.5Pointer optimization barrier"))[.](#10.sentence-2)
— *end note*]
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4064)
If a program ends the lifetime of an object of type T with
static ([[basic.stc.static]](basic.stc.static "6.8.6.2Static storage duration")), thread ([[basic.stc.thread]](basic.stc.thread "6.8.6.3Thread storage duration")),
or automatic ([[basic.stc.auto]](basic.stc.auto "6.8.6.4Automatic storage duration"))
storage duration and if T has a non-trivial destructor,[22](#footnote-22 "That is, an object for which a destructor will be called implicitly—upon exit from the block for an object with automatic storage duration, upon exit from the thread for an object with thread storage duration, or upon exit from the program for an object with static storage duration.") and another object of the original type does not occupy
that same storage location when the implicit destructor call takes
place, the behavior of the program is undefined[.](#11.sentence-1)
This is true
even if the block is exited with an exception[.](#11.sentence-2)
[*Example [4](#example-4)*: class T { };struct B {~B();};
void h() { B b; new (&b) T;} // undefined behavior at block exit — *end example*]
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4095)
Creating a new object within the storage that a const, complete
object with static, thread, or automatic storage duration occupies,
or within the storage that such a const object used to occupy before
its lifetime ended, results in undefined behavior[.](#12.sentence-1)
[*Example [5](#example-5)*: struct B { B(); ~B();};
const B b;
void h() { b.~B(); new (const_cast<B*>(&b)) const B; // undefined behavior} — *end example*]
[21)](#footnote-21)[21)](#footnoteref-21)
For example, before the dynamic initialization of
an object with static storage duration ([[basic.start.dynamic]](basic.start.dynamic "6.10.3.3Dynamic initialization of non-block variables"))[.](#footnote-21.sentence-1)
[22)](#footnote-22)[22)](#footnoteref-22)
That
is, an object for which a destructor will be called
implicitly—upon exit from the block for an object with
automatic storage duration, upon exit from the thread for an object with
thread storage duration, or upon exit from the program for an object
with static storage duration[.](#footnote-22.sentence-1)