6 Basics [basic]

6.7 Memory and objects [basic.memobj]

6.7.1 Memory model [intro.memory]

The fundamental storage unit in the C++ memory model is the byte.
A byte is at least large enough to contain any member of the basic execution character set and the eight-bit code units of the Unicode UTF-8 encoding form and is composed of a contiguous sequence of bits,27 the number of which is implementation-defined.
The least significant bit is called the low-order bit; the most significant bit is called the high-order bit.
The memory available to a C++ program consists of one or more sequences of contiguous bytes.
Every byte has a unique address.
[Note
:
The representation of types is described in [basic.types].
— end note
]
A memory location is either an object of scalar type or a maximal sequence of adjacent bit-fields all having nonzero width.
[Note
:
Various features of the language, such as references and virtual functions, might involve additional memory locations that are not accessible to programs but are managed by the implementation.
— end note
]
Two or more threads of execution can access separate memory locations without interfering with each other.
[Note
:
Thus a bit-field and an adjacent non-bit-field are in separate memory locations, and therefore can be concurrently updated by two threads of execution without interference.
The same applies to two bit-fields, if one is declared inside a nested struct declaration and the other is not, or if the two are separated by a zero-length bit-field declaration, or if they are separated by a non-bit-field declaration.
It is not safe to concurrently update two bit-fields in the same struct if all fields between them are also bit-fields of nonzero width.
— end note
]
[Example
:
A class declared as
struct {
  char a;
  int b:5,
  c:11,
  :0,
  d:8;
  struct {int ee:8;} e;
}
contains four separate memory locations: The member a and bit-fields d and e.ee are each separate memory locations, and can be modified concurrently without interfering with each other.
The bit-fields b and c together constitute the fourth memory location.
The bit-fields b and c cannot be concurrently modified, but b and a, for example, can be.
— end example
]
The number of bits in a byte is reported by the macro CHAR_­BIT in the header <climits> ([climits.syn]).

6.7.2 Object model [intro.object]

The constructs in a C++ program create, destroy, refer to, access, and manipulate objects.
An object is created by a definition, by a new-expression, by an operation that implicitly creates objects (see below), when implicitly changing the active member of a union, or when a temporary object is created ([conv.rval], [class.temporary]).
An object occupies a region of storage in its period of construction ([class.cdtor]), throughout its lifetime, and in its period of destruction ([class.cdtor]).
[Note
:
A function is not an object, regardless of whether or not it occupies storage in the way that objects do.
— end note
]
The properties of an object are determined when the object is created.
An object can have a name ([basic.pre]).
An object has a storage duration ([basic.stc]) which influences its lifetime ([basic.life]).
An object has a type ([basic.types]).
Some objects are polymorphic ([class.virtual]); the implementation generates information associated with each such object that makes it possible to determine that object's type during program execution.
For other objects, the interpretation of the values found therein is determined by the type of the expressions ([expr.compound]) used to access them.
Objects can contain other objects, called subobjects.
A subobject can be a member subobject ([class.mem]), a base class subobject ([class.derived]), or an array element.
An object that is not a subobject of any other object is called a complete object.
If an object is created in storage associated with a member subobject or array element e (which may or may not be within its lifetime), the created object is a subobject of e's containing object if:
  • the lifetime of e's containing object has begun and not ended, and
  • the storage for the new object exactly overlays the storage location associated with e, and
  • the new object is of the same type as e (ignoring cv-qualification).
If a complete object is created ([expr.new]) in storage associated with another object e of type “array of N unsigned char” or of type “array of N std​::​byte” ([cstddef.syn]), that array provides storage for the created object if:
  • the lifetime of e has begun and not ended, and
  • the storage for the new object fits entirely within e, and
  • there is no smaller array object that satisfies these constraints.
[Note
:
If that portion of the array previously provided storage for another object, the lifetime of that object ends because its storage was reused ([basic.life]).
— end note
]
[Example
:
template<typename ...T>
struct AlignedUnion {
  alignas(T...) unsigned char data[max(sizeof(T)...)];
};
int f() {
  AlignedUnion<int, char> au;
  int *p = new (au.data) int;           // OK, au.data provides storage
  char *c = new (au.data) char();       // OK, ends lifetime of *p
  char *d = new (au.data + 1) char();
  return *c + *d;                       // OK
}

struct A { unsigned char a[32]; };
struct B { unsigned char b[16]; };
A a;
B *b = new (a.a + 8) B;                 // a.a provides storage for *b
int *p = new (b->b + 4) int;            // b->b provides storage for *p
                                        // a.a does not provide storage for *p (directly),
                                        // but *p is nested within a (see below)
— end example
]
An object a is nested within another object b if:
  • a is a subobject of b, or
  • b provides storage for a, or
  • there exists an object c where a is nested within c, and c is nested within b.
For every object x, there is some object called the complete object of x, determined as follows:
  • If x is a complete object, then the complete object of x is itself.
  • Otherwise, the complete object of x is the complete object of the (unique) object that contains x.
If a complete object, a data member, or an array element is of class type, its type is considered the most derived class, to distinguish it from the class type of any base class subobject; an object of a most derived class type or of a non-class type is called a most derived object.
A potentially-overlapping subobject is either:
An object has nonzero size if it
  • is not a potentially-overlapping subobject, or
  • is not of class type, or
  • is of a class type with virtual member functions or virtual base classes, or
  • has subobjects of nonzero size or bit-fields of nonzero length.
Otherwise, if the object is a base class subobject of a standard-layout class type with no non-static data members, it has zero size.
Otherwise, the circumstances under which the object has zero size are implementation-defined.
Unless it is a bit-field, an object with nonzero size shall occupy one or more bytes of storage, including every byte that is occupied in full or in part by any of its subobjects.
An object of trivially copyable or standard-layout type ([basic.types]) shall occupy contiguous bytes of storage.
Unless an object is a bit-field or a subobject of zero size, the address of that object is the address of the first byte it occupies.
Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types; otherwise, they have distinct addresses and occupy disjoint bytes of storage.28
[Example
:
static const char test1 = 'x';
static const char test2 = 'x';
const bool b = &test1 != &test2;        // always true
— end example
]
The address of a non-bit-field subobject of zero size is the address of an unspecified byte of storage occupied by the complete object of that subobject.
Some operations are described as implicitly creating objects within a specified region of storage.
For each operation that is specified as implicitly creating objects, that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types ([basic.types]) in its specified region of storage if doing so would result in the program having defined behavior.
If no such set of objects would give the program defined behavior, the behavior of the program is undefined.
If multiple such sets of objects would give the program defined behavior, it is unspecified which such set of objects is created.
[Note
:
Such operations do not start the lifetimes of subobjects of such objects that are not themselves of implicit-lifetime types.
— end note
]
Further, after implicitly creating objects within a specified region of storage, some operations are described as producing a pointer to a suitable created object.
These operations select one of the implicitly-created objects whose address is the address of the start of the region of storage, and produce a pointer value that points to that object, if that value would result in the program having defined behavior.
If no such pointer value would give the program defined behavior, the behavior of the program is undefined.
If multiple such pointer values would give the program defined behavior, it is unspecified which such pointer value is produced.
[Example
:
#include <cstdlib>
struct X { int a, b; };
X *make_x() {
  // The call to std​::​malloc implicitly creates an object of type X
  // and its subobjects a and b, and returns a pointer to that X object
  // (or an object that is pointer-interconvertible ([basic.compound]) with it),
  // in order to give the subsequent class member access operations
  // defined behavior.
  X *p = (X*)std::malloc(sizeof(struct X));
  p->a = 1;
  p->b = 2;
  return p;
}
— end example
]
An operation that begins the lifetime of an array of char, unsigned char, or std​::​byte implicitly creates objects within the region of storage occupied by the array.
[Note
:
The array object provides storage for these objects.
— end note
]
Any implicit or explicit invocation of a function named operator new or operator new[] implicitly creates objects in the returned region of storage and returns a pointer to a suitable created object.
[Note
:
Some functions in the C++ standard library implicitly create objects ([allocator.traits.members], [c.malloc], [cstring.syn], [bit.cast]).
— end note
]
Under the “as-if” rule an implementation is allowed to store two objects at the same machine address or not store an object at all if the program cannot observe the difference ([intro.execution]).

6.7.3 Lifetime [basic.life]

The lifetime of an object or reference is a runtime property of the object or reference.
A variable is said to have vacuous initialization if it is default-initialized and, if it is of class type or a (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
  • its initialization (if any) is complete (including vacuous initialization) ([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 ([dcl.init.aggr], [class.base.init]), or as described in [class.union] and [class.copy.ctor], and except as described in [allocator.members].
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, 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]).
The lifetime of a reference begins when its initialization is complete.
The lifetime of a reference ends as if it were a scalar object requiring storage.
[Note
:
[class.base.init] describes the lifetime of base and member subobjects.
— end note
]
The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime.
[Note
:
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] and in [class.cdtor].
Also, the behavior of an object under construction and destruction might not be the same as the behavior of an object whose lifetime has started and not ended.
[class.base.init] and [class.cdtor] describe the behavior of an object during its periods of construction and destruction.
— end note
]
A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling a destructor or pseudo-destructor ([expr.prim.id.dtor]) for the object.
For an object of a class type, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression is not used to release the storage, the destructor is not implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated29 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.
For an object under construction or destruction, see [class.cdtor].
Otherwise, such a pointer refers to allocated storage ([basic.stc.dynamic.allocation]), and using the pointer as if the pointer were of type void* is well-defined.
Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below.
The program has undefined behavior if:
  • the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a delete-expression,
  • the pointer is used to access a non-static data member or call a non-static member function of the object, or
  • the pointer is implicitly converted ([conv.ptr]) to a pointer to a virtual base class, or
  • the pointer is used as the operand of a static_­cast ([expr.static.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]), or
  • the pointer is used as the operand of a dynamic_­cast ([expr.dynamic.cast]).
[Example
:
#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 memory
  void* q = pb;     // OK: pb points to valid memory
  pb->f();          // undefined behavior: lifetime of *pb has ended
}
— end example
]
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.
For an object under construction or destruction, see [class.cdtor].
Otherwise, such a glvalue refers to allocated storage ([basic.stc.dynamic.allocation]), and using the properties of the glvalue that do not depend on its value is well-defined.
The program has undefined behavior if:
  • the glvalue is used to access the object, or
  • the glvalue is used to call a non-static member function of the object, or
  • the glvalue is bound to a reference to a virtual base class ([dcl.init.ref]), or
  • the glvalue is used as the operand of a dynamic_­cast ([expr.dynamic.cast]) or as the operand of typeid.
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, 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, if the original object is transparently replaceable (see below) by the new object.
An object is transparently replaceable by an object if:
  • the storage that occupies exactly overlays the storage that occupied, and
  • and are of the same type (ignoring the top-level cv-qualifiers), and
  • is not a complete const object, and
  • neither nor is a potentially-overlapping subobject ([intro.object]), and
  • either and are both complete objects, or and are direct subobjects of objects and , respectively, and is transparently replaceable by .
[Example
:
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 ends
    new (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
:
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]).
— end note
]
If a program ends the lifetime of an object of type T with static, thread, or automatic storage duration and if T has a non-trivial destructor,30 the program must ensure that an object of the original type occupies that same storage location when the implicit destructor call takes place; otherwise the behavior of the program is undefined.
This is true even if the block is exited with an exception.
[Example
:
class T { };
struct B {
   ~B();
};

void h() {
   B b;
   new (&b) T;
}                               // undefined behavior at block exit
— end example
]
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.
[Example
:
struct B {
  B();
  ~B();
};

const B b;

void h() {
  b.~B();
  new (const_cast<B*>(&b)) const B;     // undefined behavior
}
— end example
]
In this subclause, “before” and “after” refer to the “happens before” relation ([intro.multithread]).
[Note
:
Therefore, undefined behavior results if an object that is being constructed in one thread is referenced from another thread without adequate synchronization.
— end note
]
For example, before the construction of a global object that is initialized via a user-provided constructor ([class.cdtor]).
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.

6.7.4 Indeterminate values [basic.indet]

When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value, and if no initialization is performed for the object, that object retains an indeterminate value until that value is replaced ([expr.ass]).
[Note
:
Objects with static or thread storage duration are zero-initialized, see [basic.start.static].
— end note
]
If an indeterminate value is produced by an evaluation, the behavior is undefined except in the following cases:
  • If an indeterminate value of unsigned ordinary character type ([basic.fundamental]) or std​::​byte type ([cstddef.syn]) is produced by the evaluation of: then the result of the operation is an indeterminate value.
  • If an indeterminate value of unsigned ordinary character type or std​::​byte type is produced by the evaluation of the right operand of a simple assignment operator ([expr.ass]) whose first operand is an lvalue of unsigned ordinary character type or std​::​byte type, an indeterminate value replaces the value of the object referred to by the left operand.
  • If an indeterminate value of unsigned ordinary character type is produced by the evaluation of the initialization expression when initializing an object of unsigned ordinary character type, that object is initialized to an indeterminate value.
  • If an indeterminate value of unsigned ordinary character type or std​::​byte type is produced by the evaluation of the initialization expression when initializing an object of std​::​byte type, that object is initialized to an indeterminate value.
[Example
:
int f(bool b) {
  unsigned char c;
  unsigned char d = c;          // OK, d has an indeterminate value
  int e = d;                    // undefined behavior
  return b ? d : 0;             // undefined behavior if b is true
}
— end example
]

6.7.5 Storage duration [basic.stc]

The storage duration is the property of an object that defines the minimum potential lifetime of the storage containing the object.
The storage duration is determined by the construct used to create the object and is one of the following:
  • static storage duration
  • thread storage duration
  • automatic storage duration
  • dynamic storage duration
Static, thread, and automatic storage durations are associated with objects introduced by declarations ([basic.def]) and implicitly created by the implementation.
The dynamic storage duration is associated with objects created by a new-expression.
The storage duration categories apply to references as well.
When the end of the duration of a region of storage is reached, the values of all pointers representing the address of any part of that region of storage become invalid pointer values.
Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior.
Any other use of an invalid pointer value has implementation-defined behavior.31
Some implementations might define that copying an invalid pointer value causes a system-generated runtime fault.

6.7.5.1 Static storage duration [basic.stc.static]

All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration.
The storage for these entities lasts for the duration of the program ([basic.start.static], [basic.start.term]).
If a variable with static storage duration has initialization or a destructor with side effects, it shall not be eliminated even if it appears to be unused, except that a class object or its copy/move may be eliminated as specified in [class.copy.elision].
The keyword static can be used to declare a local variable with static storage duration.
[Note
:
[stmt.dcl] describes the initialization of local static variables; [basic.start.term] describes the destruction of local static variables.
— end note
]
The keyword static applied to a class data member in a class definition gives the data member static storage duration.

6.7.5.2 Thread storage duration [basic.stc.thread]

All variables declared with the thread_­local keyword have thread storage duration.
The storage for these entities lasts for the duration of the thread in which they are created.
There is a distinct object or reference per thread, and use of the declared name refers to the entity associated with the current thread.
[Note
:
A variable with thread storage duration is initialized as specified in [basic.start.static], [basic.start.dynamic], and [stmt.dcl] and, if constructed, is destroyed on thread exit ([basic.start.term]).
— end note
]

6.7.5.3 Automatic storage duration [basic.stc.auto]

Block-scope variables not explicitly declared static, thread_­local, or extern have automatic storage duration.
The storage for these entities lasts until the block in which they are created exits.
[Note
:
These variables are initialized and destroyed as described in [stmt.dcl].
— end note
]
If a variable with automatic storage duration has initialization or a destructor with side effects, an implementation shall not destroy it before the end of its block nor eliminate it as an optimization, even if it appears to be unused, except that a class object or its copy/move may be eliminated as specified in [class.copy.elision].

6.7.5.4 Dynamic storage duration [basic.stc.dynamic]

Objects can be created dynamically during program execution, using new-expressions, and destroyed using delete-expressions.
A C++ implementation provides access to, and management of, dynamic storage via the global allocation functions operator new and operator new[] and the global deallocation functions operator delete and operator delete[].
[Note
:
The non-allocating forms described in [new.delete.placement] do not perform allocation or deallocation.
— end note
]
The library provides default definitions for the global allocation and deallocation functions.
Some global allocation and deallocation functions are replaceable ([new.delete]).
A C++ program shall provide at most one definition of a replaceable allocation or deallocation function.
Any such function definition replaces the default version provided in the library ([replacement.functions]).
The following allocation and deallocation functions ([support.dynamic]) are implicitly declared in global scope in each translation unit of a program.
[[nodiscard]] void* operator new(std::size_t);
[[nodiscard]] void* operator new(std::size_t, std::align_val_t);

void operator delete(void*) noexcept;
void operator delete(void*, std::size_t) noexcept;
void operator delete(void*, std::align_val_t) noexcept;
void operator delete(void*, std::size_t, std::align_val_t) noexcept;

[[nodiscard]] void* operator new[](std::size_t);
[[nodiscard]] void* operator new[](std::size_t, std::align_val_t);

void operator delete[](void*) noexcept;
void operator delete[](void*, std::size_t) noexcept;
void operator delete[](void*, std::align_val_t) noexcept;
void operator delete[](void*, std::size_t, std::align_val_t) noexcept;
These implicit declarations introduce only the function names operator new, operator new[], operator delete, and operator delete[].
[Note
:
The implicit declarations do not introduce the names std, std​::​size_­t, std​::​align_­val_­t, or any other names that the library uses to declare these names.
Thus, a new-expression, delete-expression, or function call that refers to one of these functions without importing or including the header <new> ([new.syn]) is well-formed.
However, referring to std or std​::​size_­t or std​::​align_­val_­t is ill-formed unless the name has been declared by importing or including the appropriate header.
— end note
]
Allocation and/or deallocation functions may also be declared and defined for any class ([class.free]).
If the behavior of an allocation or deallocation function does not satisfy the semantic constraints specified in [basic.stc.dynamic.allocation] and [basic.stc.dynamic.deallocation], the behavior is undefined.

6.7.5.4.1 Allocation functions [basic.stc.dynamic.allocation]

An allocation function shall be a class member function or a global function; a program is ill-formed if an allocation function is declared in a namespace scope other than global scope or declared static in global scope.
The return type shall be void*.
The first parameter shall have type std​::​size_­t ([support.types]).
The first parameter shall not have an associated default argument ([dcl.fct.default]).
The value of the first parameter is interpreted as the requested size of the allocation.
An allocation function can be a function template.
Such a template shall declare its return type and first parameter as specified above (that is, template parameter types shall not be used in the return type and first parameter type).
Template allocation functions shall have two or more parameters.
An allocation function attempts to allocate the requested amount of storage.
If it is successful, it returns the address of the start of a block of storage whose length in bytes is at least as large as the requested size.
The order, contiguity, and initial value of storage allocated by successive calls to an allocation function are unspecified.
Even if the size of the space requested is zero, the request can fail.
If the request succeeds, the value returned by a replaceable allocation function is a non-null pointer value ([basic.compound]) p0 different from any previously returned value p1, unless that value p1 was subsequently passed to a replaceable deallocation function.
Furthermore, for the library allocation functions in [new.delete.single] and [new.delete.array], p0 represents the address of a block of storage disjoint from the storage for any other object accessible to the caller.
The effect of indirecting through a pointer returned from a request for zero size is undefined.32
For an allocation function other than a reserved placement allocation function ([new.delete.placement]), the pointer returned on a successful call shall represent the address of storage that is aligned as follows:
  • If the allocation function takes an argument of type std​::​align_­val_­t, the storage will have the alignment specified by the value of this argument.
  • Otherwise, if the allocation function is named operator new[], the storage is aligned for any object that does not have new-extended alignment ([basic.align]) and is no larger than the requested size.
  • Otherwise, the storage is aligned for any object that does not have new-extended alignment and is of the requested size.
An allocation function that fails to allocate storage can invoke the currently installed new-handler function ([new.handler]), if any.
[Note
:
A program-supplied allocation function can obtain the address of the currently installed new_­handler using the std​::​get_­new_­handler function ([get.new.handler]).
— end note
]
An allocation function that has a non-throwing exception specification ([except.spec]) indicates failure by returning a null pointer value.
Any other allocation function never returns a null pointer value and indicates failure only by throwing an exception ([except.throw]) of a type that would match a handler ([except.handle]) of type std​::​bad_­alloc ([bad.alloc]).
A global allocation function is only called as the result of a new expression, or called directly using the function call syntax, or called indirectly to allocate storage for a coroutine state ([dcl.fct.def.coroutine]), or called indirectly through calls to the functions in the C++ standard library.
[Note
:
In particular, a global allocation function is not called to allocate storage for objects with static storage duration, for objects or references with thread storage duration, for objects of type std​::​type_­info, or for an exception object.
— end note
]
The intent is to have operator new() implementable by calling std​::​malloc() or std​::​calloc(), so the rules are substantially the same.
C++ differs from C in requiring a zero request to return a non-null pointer.

6.7.5.4.2 Deallocation functions [basic.stc.dynamic.deallocation]

Deallocation functions shall be class member functions or global functions; a program is ill-formed if deallocation functions are declared in a namespace scope other than global scope or declared static in global scope.
A deallocation function is a destroying operator delete if it has at least two parameters and its second parameter is of type std​::​destroying_­delete_­t.
A destroying operator delete shall be a class member function named operator delete.
[Note
:
Array deletion cannot use a destroying operator delete.
— end note
]
Each deallocation function shall return void.
If the function is a destroying operator delete declared in class type C, the type of its first parameter shall be C*; otherwise, the type of its first parameter shall be void*.
A deallocation function may have more than one parameter.
A usual deallocation function is a deallocation function whose parameters after the first are
  • optionally, a parameter of type std​::​destroying_­delete_­t, then
  • optionally, a parameter of type std​::​size_­t33, then
  • optionally, a parameter of type std​::​align_­val_­t.
A destroying operator delete shall be a usual deallocation function.
A deallocation function may be an instance of a function template.
Neither the first parameter nor the return type shall depend on a template parameter.
A deallocation function template shall have two or more function parameters.
A template instance is never a usual deallocation function, regardless of its signature.
If a deallocation function terminates by throwing an exception, the behavior is undefined.
The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect.
If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value ([basic.compound]), the deallocation function shall deallocate the storage referenced by the pointer, ending the duration of the region of storage.
The global operator delete(void*, std​::​size_­t) precludes use of an allocation function void operator new(std​::​size_­t, std​::​size_­t) as a placement allocation function ([basic]: basics'>[diff.cpp11.basic]).

6.7.5.4.3 Safely-derived pointers [basic.stc.dynamic.safety]

A traceable pointer object is
  • an object of an object pointer type, or
  • an object of an integral type that is at least as large as std​::​intptr_­t, or
  • a sequence of elements in an array of narrow character type, where the size and alignment of the sequence match those of some object pointer type.
A pointer value is a safely-derived pointer to an object with dynamic storage duration only if the pointer value has an object pointer type and is one of the following:
  • the value returned by a call to the C++ standard library implementation of ​::​operator new(std​::​​size_­t) or ​::​operator new(std​::​size_­t, std​::​align_­val_­t);34
  • the result of taking the address of an object (or one of its subobjects) designated by an lvalue resulting from indirection through a safely-derived pointer value;
  • the result of well-defined pointer arithmetic ([expr.add]) using a safely-derived pointer value;
  • the result of a well-defined pointer conversion ([conv.ptr], [expr.type.conv], [expr.static.cast], [expr.cast]) of a safely-derived pointer value;
  • the result of a reinterpret_­cast of a safely-derived pointer value;
  • the result of a reinterpret_­cast of an integer representation of a safely-derived pointer value;
  • the value of an object whose value was copied from a traceable pointer object, where at the time of the copy the source object contained a copy of a safely-derived pointer value.
An integer value is an integer representation of a safely-derived pointer only if its type is at least as large as std​::​intptr_­t and it is one of the following:
  • the result of a reinterpret_­cast of a safely-derived pointer value;
  • the result of a valid conversion of an integer representation of a safely-derived pointer value;
  • the value of an object whose value was copied from a traceable pointer object, where at the time of the copy the source object contained an integer representation of a safely-derived pointer value;
  • the result of an additive or bitwise operation, one of whose operands is an integer representation of a safely-derived pointer value P, if that result converted by reinterpret_­cast<void*> would compare equal to a safely-derived pointer computable from reinterpret_­cast<void*>(P).
An implementation may have relaxed pointer safety, in which case the validity of a pointer value does not depend on whether it is a safely-derived pointer value.
Alternatively, an implementation may have strict pointer safety, in which case a pointer value referring to an object with dynamic storage duration that is not a safely-derived pointer value is an invalid pointer value unless the referenced complete object has previously been declared reachable ([util.dynamic.safety]).
[Note
:
The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined, see [basic.stc].
This is true even if the unsafely-derived pointer value might compare equal to some safely-derived pointer value.
— end note
]
It is implementation-defined whether an implementation has relaxed or strict pointer safety.
This subclause does not impose restrictions on indirection through pointers to memory not allocated by ​::​operator new.
This maintains the ability of many C++ implementations to use binary libraries and components written in other languages.
In particular, this applies to C binaries, because indirection through pointers to memory allocated by std​::​malloc is not restricted.

6.7.5.5 Duration of subobjects [basic.stc.inherit]

The storage duration of subobjects and reference members is that of their complete object ([intro.object]).

6.7.6 Alignment [basic.align]

Object types have alignment requirements ([basic.fundamental], [basic.compound]) which place restrictions on the addresses at which an object of that type may be allocated.
An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated.
An object type imposes an alignment requirement on every object of that type; stricter alignment can be requested using the alignment specifier.
A fundamental alignment is represented by an alignment less than or equal to the greatest alignment supported by the implementation in all contexts, which is equal to alignof(std​::​max_­align_­t) ([support.types]).
The alignment required for a type might be different when it is used as the type of a complete object and when it is used as the type of a subobject.
[Example
:
struct B { long double d; };
struct D : virtual B { char c; };
When D is the type of a complete object, it will have a subobject of type B, so it must be aligned appropriately for a long double.
If D appears as a subobject of another object that also has B as a virtual base class, the B subobject might be part of a different subobject, reducing the alignment requirements on the D subobject.
— end example
]
The result of the alignof operator reflects the alignment requirement of the type in the complete-object case.
An extended alignment is represented by an alignment greater than alignof(std​::​max_­align_­t).
It is implementation-defined whether any extended alignments are supported and the contexts in which they are supported ([dcl.align]).
A type having an extended alignment requirement is an over-aligned type.
[Note
:
Every over-aligned type is or contains a class type to which extended alignment applies (possibly through a non-static data member).
— end note
]
A new-extended alignment is represented by an alignment greater than __STDCPP_­DEFAULT_­NEW_­ALIGNMENT__ ([cpp.predefined]).
Alignments are represented as values of the type std​::​size_­t.
Valid alignments include only those values returned by an alignof expression for the fundamental types plus an additional implementation-defined set of values, which may be empty.
Every alignment value shall be a non-negative integral power of two.
Alignments have an order from weaker to stronger or stricter alignments.
Stricter alignments have larger alignment values.
An address that satisfies an alignment requirement also satisfies any weaker valid alignment requirement.
The alignment requirement of a complete type can be queried using an alignof expression.
Furthermore, the narrow character types shall have the weakest alignment requirement.
[Note
:
This enables the ordinary character types to be used as the underlying type for an aligned memory area ([dcl.align]).
— end note
]
Comparing alignments is meaningful and provides the obvious results:
  • Two alignments are equal when their numeric values are equal.
  • Two alignments are different when their numeric values are not equal.
  • When an alignment is larger than another it represents a stricter alignment.
[Note
:
The runtime pointer alignment function ([ptr.align]) can be used to obtain an aligned pointer within a buffer; the aligned-storage templates in the library ([meta.trans.other]) can be used to obtain aligned storage.
— end note
]
If a request for a specific extended alignment in a specific context is not supported by an implementation, the program is ill-formed.

6.7.7 Temporary objects [class.temporary]

Temporary objects are created
  • when a prvalue is converted to an xvalue ([conv.rval]),
  • when needed by the implementation to pass or return an object of trivially copyable type (see below), and
  • when throwing an exception ([except.throw]).
    [Note
    : The lifetime of exception objects is described in [except.throw]. — end note
    ]
Even when the creation of the temporary object is unevaluated ([expr.prop]), all the semantic restrictions shall be respected as if the temporary object had been created and later destroyed.
[Note
:
This includes accessibility ([class.access]) and whether it is deleted, for the constructor selected and for the destructor.
However, in the special case of the operand of a decltype-specifier, no temporary is introduced, so the foregoing does not apply to such a prvalue.
— end note
]
The materialization of a temporary object is generally delayed as long as possible in order to avoid creating unnecessary temporary objects.
[Note
:
Temporary objects are materialized:
— end note
]
[Example
:
Consider the following code:
class X {
public:
  X(int);
  X(const X&);
  X& operator=(const X&);
  ~X();
};

class Y {
public:
  Y(int);
  Y(Y&&);
  ~Y();
};

X f(X);
Y g(Y);

void h() {
  X a(1);
  X b = f(X(2));
  Y c = g(Y(3));
  a = f(a);
}
X(2) is constructed in the space used to hold f()'s argument and Y(3) is constructed in the space used to hold g()'s argument.
Likewise, f()'s result is constructed directly in b and g()'s result is constructed directly in c.
On the other hand, the expression a = f(a) requires a temporary for the result of f(a), which is materialized so that the reference parameter of X​::​operator=(const X&) can bind to it.
— end example
]
When an object of class type X is passed to or returned from a function, if X has at least one eligible copy or move constructor ([special]), each such constructor is trivial, and the destructor of X is either trivial or deleted, implementations are permitted to create a temporary object to hold the function parameter or result object.
The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the eligible trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object).
[Note
:
This latitude is granted to allow objects of class type to be passed to or returned from functions in registers.
— end note
]
When an implementation introduces a temporary object of a class that has a non-trivial constructor ([class.default.ctor], [class.copy.ctor]), it shall ensure that a constructor is called for the temporary object.
Similarly, the destructor shall be called for a temporary with a non-trivial destructor ([class.dtor]).
Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created.
This is true even if that evaluation ends in throwing an exception.
The value computations and side effects of destroying a temporary object are associated only with the full-expression, not with any specific subexpression.
There are three contexts in which temporaries are destroyed at a different point than the end of the full-expression.
The first context is when a default constructor is called to initialize an element of an array with no corresponding initializer ([dcl.init]).
The second context is when a copy constructor is called to copy an element of an array while the entire array is copied ([expr.prim.lambda.capture], [class.copy.ctor]).
In either case, if the constructor has one or more default arguments, the destruction of every temporary created in a default argument is sequenced before the construction of the next array element, if any.
The third context is when a reference is bound to a temporary object.35
The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference if the glvalue to which the reference is bound was obtained through one of the following:
  • a temporary materialization conversion ([conv.rval]),
  • ( expression ), where expression is one of these expressions,
  • subscripting ([expr.sub]) of an array operand, where that operand is one of these expressions,
  • a class member access ([expr.ref]) using the . operator where the left operand is one of these expressions and the right operand designates a non-static data member of non-reference type,
  • a pointer-to-member operation ([expr.mptr.oper]) using the .* operator where the left operand is one of these expressions and the right operand is a pointer to data member of non-reference type,
  • a converting, without a user-defined conversion, a glvalue operand that is one of these expressions to a glvalue that refers to the object designated by the operand, or to its complete object or a subobject thereof,
  • a conditional expression ([expr.cond]) that is a glvalue where the second or third operand is one of these expressions, or
  • a comma expression ([expr.comma]) that is a glvalue where the right operand is one of these expressions.
[Example
:
template<typename T> using id = T;

int i = 1;
int&& a = id<int[3]>{1, 2, 3}[i];           // temporary array has same lifetime as a
const int& b = static_cast<const int&>(0);  // temporary int has same lifetime as b
int&& c = cond ? id<int[3]>{1, 2, 3}[i] : static_cast<int&&>(0);
                                            // exactly one of the two temporaries is lifetime-extended
— end example
]
[Note
:
An explicit type conversion ([expr.type.conv], [expr.cast]) is interpreted as a sequence of elementary casts, covered above.
[Example
:
const int& x = (const int&)1;   // temporary for value 1 has same lifetime as x
— end example
]
— end note
]
[Note
:
If a temporary object has a reference member initialized by another temporary object, lifetime extension applies recursively to such a member's initializer.
[Example
:
struct S {
  const int& m;
};
const S& s = S{1};              // both S and int temporaries have lifetime of s
— end example
]
— end note
]
The exceptions to this lifetime rule are:
  • A temporary object bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.
  • A temporary object bound to a reference element of an aggregate of class type initialized from a parenthesized expression-list ([dcl.init]) persists until the completion of the full-expression containing the expression-list.
  • The lifetime of a temporary bound to the returned value in a function return statement ([stmt.return]) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
  • A temporary bound to a reference in a new-initializer persists until the completion of the full-expression containing the new-initializer.
    [Note
    : This may introduce a dangling reference. — end note
    ]
    [Example
    :
    struct S { int mi; const std::pair<int,int>& mp; };
    S a { 1, {2,3} };
    S* p = new S{ 1, {2,3} };       // creates dangling reference
    
    — end example
    ]
The destruction of a temporary whose lifetime is not extended by being bound to a reference is sequenced before the destruction of every temporary which is constructed earlier in the same full-expression.
If the lifetime of two or more temporaries to which references are bound ends at the same point, these temporaries are destroyed at that point in the reverse order of the completion of their construction.
In addition, the destruction of temporaries bound to references shall take into account the ordering of destruction of objects with static, thread, or automatic storage duration ([basic.stc.static], [basic.stc.thread], [basic.stc.auto]); that is, if obj1 is an object with the same storage duration as the temporary and created before the temporary is created the temporary shall be destroyed before obj1 is destroyed; if obj2 is an object with the same storage duration as the temporary and created after the temporary is created the temporary shall be destroyed after obj2 is destroyed.
[Example
:
struct S {
  S();
  S(int);
  friend S operator+(const S&, const S&);
  ~S();
};
S obj1;
const S& cr = S(16)+S(23);
S obj2;
The expression S(16) + S(23) creates three temporaries: a first temporary T1 to hold the result of the expression S(16), a second temporary T2 to hold the result of the expression S(23), and a third temporary T3 to hold the result of the addition of these two expressions.
The temporary T3 is then bound to the reference cr.
It is unspecified whether T1 or T2 is created first.
On an implementation where T1 is created before T2, T2 shall be destroyed before T1.
The temporaries T1 and T2 are bound to the reference parameters of operator+; these temporaries are destroyed at the end of the full-expression containing the call to operator+.
The temporary T3 bound to the reference cr is destroyed at the end of cr's lifetime, that is, at the end of the program.
In addition, the order in which T3 is destroyed takes into account the destruction order of other objects with static storage duration.
That is, because obj1 is constructed before T3, and T3 is constructed before obj2, obj2 shall be destroyed before T3, and T3 shall be destroyed before obj1.
— end example
]
The same rules apply to initialization of an initializer_­list object ([dcl.init.list]) with its underlying temporary array.