7 Expressions [expr]

7.6 Compound expressions [expr.compound]

7.6.2 Unary expressions [expr.unary]

7.6.2.1 General [expr.unary.general]

7.6.2.2 Unary operators [expr.unary.op]

The unary * operator performs indirection.
Its operand shall be a prvalue of type “pointer to T”, where T is an object or function type.
The operator yields an lvalue of type T.
If the operand points to an object or function, the result denotes that object or function; otherwise, the behavior is undefined except as specified in [expr.typeid].
[Note 1: 
Indirection through a pointer to an incomplete type (other than cv void) is valid.
The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a prvalue, see [conv.lval].
— end note]
Each of the following unary operators yields a prvalue.
The operand of the unary & operator shall be an lvalue of some type T.
  • If the operand is a qualified-id naming a non-static or variant member m of some class C, other than an explicit object member function, the result has type “pointer to member of class C of type T” and designates C​::​m.
  • Otherwise, the result has type “pointer to T” and points to the designated object ([intro.memory]) or function ([basic.compound]).
    If the operand names an explicit object member function ([dcl.fct]), the operand shall be a qualified-id.
    [Note 2: 
    In particular, taking the address of a variable of type “cv T” yields a pointer of type “pointer to cv T.
    — end note]
[Example 1: struct A { int i; }; struct B : A { }; ... &B::i ... // has type int A​::​* int a; int* p1 = &a; int* p2 = p1 + 1; // defined behavior bool b = p2 > p1; // defined behavior, with value true — end example]
[Note 3: 
A pointer to member formed from a mutable non-static data member ([dcl.stc]) does not reflect the mutable specifier associated with the non-static data member.
— end note]
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses.
[Note 4: 
That is, the expression &(qualified-id), where the qualified-id is enclosed in parentheses, does not form an expression of type “pointer to member”.
Neither does qualified-id, because there is no implicit conversion from a qualified-id for a non-static member function to the type “pointer to member function” as there is from an lvalue of function type to the type “pointer to function” ([conv.func]).
Nor is &unqualified-id a pointer to member, even within the scope of the unqualified-id's class.
— end note]
If & is applied to an lvalue of incomplete class type and the complete type declares operator&(), it is unspecified whether the operator has the built-in meaning or the operator function is called.
The operand of & shall not be a bit-field.
[Note 5: 
The address of an overload set ([over]) can be taken only in a context that uniquely determines which function is referred to (see [over.over]).
Since the context can affect whether the operand is a static or non-static member function, the context can also affect whether the expression has type “pointer to function” or “pointer to member function”.
— end note]
The operand of the unary + operator shall be a prvalue of arithmetic, unscoped enumeration, or pointer type and the result is the value of the argument.
Integral promotion is performed on integral or enumeration operands.
The type of the result is the type of the promoted operand.
The operand of the unary - operator shall be a prvalue of arithmetic or unscoped enumeration type and the result is the negative of its operand.
Integral promotion is performed on integral or enumeration operands.
The negative of an unsigned quantity is computed by subtracting its value from , where n is the number of bits in the promoted operand.
The type of the result is the type of the promoted operand.
[Note 6: 
The result is the two's complement of the operand (where operand and result are considered as unsigned).
— end note]
The operand of the logical negation operator ! is contextually converted to bool ([conv]); its value is true if the converted operand is false and false otherwise.
The type of the result is bool.
The operand of the ~ operator shall be a prvalue of integral or unscoped enumeration type.
Integral promotions are performed.
The type of the result is the type of the promoted operand.
Given the coefficients of the base-2 representation ([basic.fundamental]) of the promoted operand x, the coefficient of the base-2 representation of the result r is 1 if is 0, and 0 otherwise.
[Note 7: 
The result is the ones' complement of the operand (where operand and result are considered as unsigned).
— end note]
There is an ambiguity in the grammar when ~ is followed by a type-name or computed-type-specifier.
The ambiguity is resolved by treating ~ as the operator rather than as the start of an unqualified-id naming a destructor.
[Note 8: 
Because the grammar does not permit an operator to follow the ., ->, or ​::​ tokens, a ~ followed by a type-name or computed-type-specifier in a member access expression or qualified-id is unambiguously parsed as a destructor name.
— end note]

7.6.2.3 Increment and decrement [expr.pre.incr]

The operand of prefix ++ or -- shall not be of type cv bool.
An operand with volatile-qualified type is deprecated; see [depr.volatile.type].
The expression ++x is otherwise equivalent to x+=1 and the expression --x is otherwise equivalent to x-=1 ([expr.ass]).
[Note 1: 
For postfix increment and decrement, see [expr.post.incr].
— end note]

7.6.2.4 Await [expr.await]

The co_await expression is used to suspend evaluation of a coroutine ([dcl.fct.def.coroutine]) while awaiting completion of the computation represented by the operand expression.
Suspending the evaluation of a coroutine transfers control to its caller or resumer.
An await-expression shall appear only as a potentially-evaluated expression within the compound-statement of a function-body or lambda-expression, in either case outside of a handler ([except.pre]).
An await-expression shall not appear in a default argument ([dcl.fct.default]).
An await-expression shall not appear in the initializer of a block variable with static or thread storage duration.
A context within a function where an await-expression can appear is called a suspension context of the function.
Evaluation of an await-expression involves the following auxiliary types, expressions, and objects:
  • p is an lvalue naming the promise object ([dcl.fct.def.coroutine]) of the enclosing coroutine and P is the type of that object.
  • Unless the await-expression was implicitly produced by a yield-expression ([expr.yield]), an initial await expression, or a final await expression ([dcl.fct.def.coroutine]), a search is performed for the name await_transform in the scope of P ([class.member.lookup]).
    If this search is performed and finds at least one declaration, then a is p.await_transform(cast-expression); otherwise, a is the cast-expression.
  • o is determined by enumerating the applicable operator co_await functions for an argument a ([over.match.oper]), and choosing the best one through overload resolution ([over.match]).
    If overload resolution is ambiguous, the program is ill-formed.
    If no viable functions are found, o is a.
    Otherwise, o is a call to the selected function with the argument a.
    If o would be a prvalue, the temporary materialization conversion ([conv.rval]) is applied.
  • e is an lvalue referring to the result of evaluating the (possibly-converted) o.
  • h is an object of type std​::​coroutine_handle<P> referring to the enclosing coroutine.
  • await-ready is the expression e.await_ready(), contextually converted to bool.
  • await-suspend is the expression e.await_suspend(h), which shall be a prvalue of type void, bool, or std​::​coroutine_handle<Z> for some type Z.
  • await-resume is the expression e.await_resume().
The await-expression has the same type and value category as the await-resume expression.
The await-expression evaluates the (possibly-converted) o expression and the await-ready expression, then:
  • If the result of await-ready is false, the coroutine is considered suspended.
    Then:
    • If the type of await-suspend is std​::​coroutine_handle<Z>, await-suspend.resume() is evaluated.
      [Note 1: 
      This resumes the coroutine referred to by the result of await-suspend.
      Any number of coroutines can be successively resumed in this fashion, eventually returning control flow to the current coroutine caller or resumer ([dcl.fct.def.coroutine]).
      — end note]
    • Otherwise, if the type of await-suspend is bool, await-suspend is evaluated, and the coroutine is resumed if the result is false.
    • Otherwise, await-suspend is evaluated.
    If the evaluation of await-suspend exits via an exception, the exception is caught, the coroutine is resumed, and the exception is immediately rethrown ([except.throw]).
    Otherwise, control flow returns to the current coroutine caller or resumer ([dcl.fct.def.coroutine]) without exiting any scopes ([stmt.jump]).
    The point in the coroutine immediately prior to control returning to its caller or resumer is a coroutine suspend point.
  • If the result of await-ready is true, or when the coroutine is resumed other than by rethrowing an exception from await-suspend, the await-resume expression is evaluated, and its result is the result of the await-expression.
[Note 2: 
With respect to sequencing, an await-expression is indivisible ([intro.execution]).
— end note]
[Example 1: template <typename T> struct my_future { /* ... */ bool await_ready(); void await_suspend(std::coroutine_handle<>); T await_resume(); }; template <class Rep, class Period> auto operator co_await(std::chrono::duration<Rep, Period> d) { struct awaiter { std::chrono::system_clock::duration duration; /* ... */ awaiter(std::chrono::system_clock::duration d) : duration(d) {} bool await_ready() const { return duration.count() <= 0; } void await_resume() {} void await_suspend(std::coroutine_handle<> h) { /* ... */ } }; return awaiter{d}; } using namespace std::chrono; my_future<int> h(); my_future<void> g() { std::cout << "just about to go to sleep...\n"; co_await 10ms; std::cout << "resumed\n"; co_await h(); } auto f(int x = co_await h()); // error: await-expression outside of function suspension context int a[] = { co_await h() }; // error: await-expression outside of function suspension context — end example]

7.6.2.5 Sizeof [expr.sizeof]

The sizeof operator yields the number of bytes occupied by a non-potentially-overlapping object of the type of its operand.
The operand is either an expression, which is an unevaluated operand, or a parenthesized type-id.
The sizeof operator shall not be applied to an expression that has function or incomplete type, to the parenthesized name of such types, or to a glvalue that designates a bit-field.
The result of sizeof applied to any of the narrow character types is 1.
The result of sizeof applied to any other fundamental type ([basic.fundamental]) is implementation-defined.
[Note 1: 
In particular, the values of sizeof(bool), sizeof(char16_t), sizeof(char32_t), and sizeof(wchar_t) are implementation-defined.58
— end note]
[Note 2: 
See [intro.memory] for the definition of byte and [basic.types.general] for the definition of object representation.
— end note]
When applied to a reference type, the result is the size of the referenced type.
When applied to a class, the result is the number of bytes in an object of that class including any padding required for placing objects of that type in an array.
The result of applying sizeof to a potentially-overlapping subobject is the size of the type, not the size of the subobject.59
When applied to an array, the result is the total number of bytes in the array.
This implies that the size of an array of n elements is n times the size of an element.
The lvalue-to-rvalue ([conv.lval]), array-to-pointer ([conv.array]), and function-to-pointer ([conv.func]) standard conversions are not applied to the operand of sizeof.
If the operand is a prvalue, the temporary materialization conversion is applied.
The identifier in a sizeof... expression shall name a pack.
The sizeof... operator yields the number of elements in the pack ([temp.variadic]).
A sizeof... expression is a pack expansion ([temp.variadic]).
[Example 1: template<class... Types> struct count { static constexpr std::size_t value = sizeof...(Types); }; — end example]
The result of sizeof and sizeof... is a prvalue of type std​::​size_t.
[Note 3: 
A sizeof expression is an integral constant expression ([expr.const]).
The type std​::​size_t is defined in the standard header <cstddef> ([cstddef.syn], [support.types.layout]).
— end note]
58)58)
sizeof(bool) is not required to be 1.
59)59)
The actual size of a potentially-overlapping subobject can be less than the result of applying sizeof to the subobject, due to virtual base classes and less strict padding requirements on potentially-overlapping subobjects.

7.6.2.6 Alignof [expr.alignof]

An alignof expression yields the alignment requirement of its operand type.
The operand shall be a type-id representing a complete object type, or an array thereof, or a reference to one of those types.
The result is a prvalue of type std​::​size_t.
[Note 1: 
An alignof expression is an integral constant expression ([expr.const]).
The type std​::​size_t is defined in the standard header <cstddef> ([cstddef.syn], [support.types.layout]).
— end note]
When alignof is applied to a reference type, the result is the alignment of the referenced type.
When alignof is applied to an array type, the result is the alignment of the element type.

7.6.2.7 noexcept operator [expr.unary.noexcept]

The operand of the noexcept operator is an unevaluated operand ([expr.context]).
If the operand is a prvalue, the temporary materialization conversion ([conv.rval]) is applied.
The result of the noexcept operator is a prvalue of type bool.
The result is false if the full-expression of the operand is potentially-throwing ([except.spec]), and true otherwise.
[Note 1: 
A noexcept-expression is an integral constant expression ([expr.const]).
— end note]

7.6.2.8 New [expr.new]

The new-expression attempts to create an object of the type-id or new-type-id ([dcl.name]) to which it is applied.
The type of that object is the allocated type.
This type shall be a complete object type ([basic.types.general]), but not an abstract class type ([class.abstract]) or array thereof ([intro.object]).
[Note 1: 
Because references are not objects, references cannot be created by new-expressions.
— end note]
[Note 2: 
The type-id can be a cv-qualified type, in which case the object created by the new-expression has a cv-qualified type.
— end note]
If a placeholder type or a placeholder for a deduced class type ([dcl.type.class.deduct]) appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the allocated type is deduced as follows: Let init be the new-initializer, if any, and T be the new-type-id or type-id of the new-expression, then the allocated type is the type deduced for the variable x in the invented declaration ([dcl.spec.auto]): T x init ;
[Example 1: new auto(1); // allocated type is int auto x = new auto('a'); // allocated type is char, x is of type char* template<class T> struct A { A(T, T); }; auto y = new A{1, 2}; // allocated type is A<int> — end example]
The new-type-id in a new-expression is the longest possible sequence of new-declarators.
[Note 3: 
This prevents ambiguities between the declarator operators &, &&, *, and [] and their expression counterparts.
— end note]
[Example 2: new int * i; // syntax error: parsed as (new int*) i, not as (new int)*i
The * is the pointer declarator and not the multiplication operator.
— end example]
[Note 4: 
Parentheses in a new-type-id of a new-expression can have surprising effects.
[Example 3: 
new int(*[10])(); // error is ill-formed because the binding is (new int) (*[10])(); // error
Instead, the explicitly parenthesized version of the new operator can be used to create objects of compound types ([basic.compound]):
new (int (*[10])()); allocates an array of 10 pointers to functions (taking no argument and returning int).
— end example]
— end note]
The attribute-specifier-seq in a noptr-new-declarator appertains to the associated array type.
Every constant-expression in a noptr-new-declarator shall be a converted constant expression ([expr.const]) of type std​::​size_t and its value shall be greater than zero.
[Example 4: 
Given the definition int n = 42, new float[n][5] is well-formed (because n is the expression of a noptr-new-declarator), but new float[5][n] is ill-formed (because n is not a constant expression).
— end example]
If the type-id or new-type-id denotes an array type of unknown bound ([dcl.array]), the new-initializer shall not be omitted; the allocated object is an array with n elements, where n is determined from the number of initial elements supplied in the new-initializer ([dcl.init.aggr], [dcl.init.string]).
If the expression in a noptr-new-declarator is present, it is implicitly converted to std​::​size_t.
The value of the expression is invalid if
  • the expression is of non-class type and its value before converting to std​::​size_t is less than zero;
  • the expression is of class type and its value before application of the second standard conversion ([over.ics.user])60 is less than zero;
  • its value is such that the size of the allocated object would exceed the implementation-defined limit; or
  • the new-initializer is a braced-init-list and the number of array elements for which initializers are provided (including the terminating '\0' in a string-literal ([lex.string])) exceeds the number of elements to initialize.
If the value of the expression is invalid after converting to std​::​size_t:
When the value of the expression is zero, the allocation function is called to allocate an array with no elements.
If the allocated type is an array, the new-initializer is a braced-init-list, and the expression is potentially-evaluated and not a core constant expression, the semantic constraints of copy-initializing a hypothetical element of the array from an empty initializer list are checked ([dcl.init.list]).
[Note 5: 
The array can contain more elements than there are elements in the braced-init-list, requiring initialization of the remainder of the array elements from an empty initializer list.
— end note]
Objects created by a new-expression have dynamic storage duration ([basic.stc.dynamic]).
[Note 6: 
The lifetime of such an object is not necessarily restricted to the scope in which it is created.
— end note]
When the allocated type is “array of N T” (that is, the noptr-new-declarator syntax is used or the new-type-id or type-id denotes an array type), the new-expression yields a prvalue of type “pointer to T” that points to the initial element (if any) of the array.
Otherwise, let T be the allocated type; the new-expression is a prvalue of type “pointer to T” that points to the object created.
[Note 7: 
Both new int and new int[10] have type int* and the type of new int[i][10] is int (*)[10].
— end note]
A new-expression may obtain storage for the object by calling an allocation function ([basic.stc.dynamic.allocation]).
If the new-expression terminates by throwing an exception, it may release storage by calling a deallocation function.
If the allocated type is a non-array type, the allocation function's name is operator new and the deallocation function's name is operator delete.
If the allocated type is an array type, the allocation function's name is operator new[] and the deallocation function's name is operator delete[].
[Note 8: 
An implementation is expected to provide default definitions for the global allocation functions ([basic.stc.dynamic], [new.delete.single], [new.delete.array]).
A C++ program can provide alternative definitions of these functions ([replacement.functions]) and/or class-specific versions ([class.free]).
The set of allocation and deallocation functions that can be called by a new-expression can include functions that do not perform allocation or deallocation; for example, see [new.delete.placement].
— end note]
If the new-expression does not begin with a unary ​::​ operator and the allocated type is a class type T or array thereof, a search is performed for the allocation function's name in the scope of T ([class.member.lookup]).
Otherwise, or if nothing is found, the allocation function's name is looked up by searching for it in the global scope.
An implementation is allowed to omit a call to a replaceable global allocation function ([new.delete.single], [new.delete.array]).
When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression.
During an evaluation of a constant expression, a call to a replaceable allocation function is always omitted ([expr.const]).
The implementation may extend the allocation of a new-expression e1 to provide storage for a new-expression e2 if the following would be true were the allocation not extended:
  • the evaluation of e1 is sequenced before the evaluation of e2, and
  • e2 is evaluated whenever e1 obtains storage, and
  • both e1 and e2 invoke the same replaceable global allocation function, and
  • if the allocation function invoked by e1 and e2 is throwing, any exceptions thrown in the evaluation of either e1 or e2 would be first caught in the same handler, and
  • the pointer values produced by e1 and e2 are operands to evaluated delete-expressions, and
  • the evaluation of e2 is sequenced before the evaluation of the delete-expression whose operand is the pointer value produced by e1.
[Example 5: void can_merge(int x) { // These allocations are safe for merging: std::unique_ptr<char[]> a{new (std::nothrow) char[8]}; std::unique_ptr<char[]> b{new (std::nothrow) char[8]}; std::unique_ptr<char[]> c{new (std::nothrow) char[x]}; g(a.get(), b.get(), c.get()); } void cannot_merge(int x) { std::unique_ptr<char[]> a{new char[8]}; try { // Merging this allocation would change its catch handler. std::unique_ptr<char[]> b{new char[x]}; } catch (const std::bad_alloc& e) { std::cerr << "Allocation failed: " << e.what() << std::endl; throw; } } — end example]
When a new-expression calls an allocation function and that allocation has not been extended, the new-expression passes the amount of space requested to the allocation function as the first argument of type std​::​size_t.
That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array and the allocation function is not a non-allocating form ([new.delete.placement]).
For arrays of char, unsigned char, and std​::​byte, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement of any object type whose size is no greater than the size of the array being created.
[Note 9: 
Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating character arrays into which objects of other types will later be placed.
— end note]
When a new-expression calls an allocation function and that allocation has been extended, the size argument to the allocation call shall be no greater than the sum of the sizes for the omitted calls as specified above, plus the size for the extended call had it not been extended, plus any padding necessary to align the allocated objects within the allocated memory.
The new-placement syntax is used to supply additional arguments to an allocation function; such an expression is called a placement new-expression.
Overload resolution is performed on a function call created by assembling an argument list.
The first argument is the amount of space requested, and has type std​::​size_t.
If the type of the allocated object has new-extended alignment, the next argument is the type's alignment, and has type std​::​align_val_t.
If the new-placement syntax is used, the initializer-clauses in its expression-list are the succeeding arguments.
If no matching function is found then
  • if the allocated object type has new-extended alignment, the alignment argument is removed from the argument list;
  • otherwise, an argument that is the type's alignment and has type std​::​align_val_t is added into the argument list immediately after the first argument;
and then overload resolution is performed again.
[Example 6: 
  • new T results in one of the following calls: operator new(sizeof(T)) operator new(sizeof(T), std::align_val_t(alignof(T)))
  • new(2,f) T results in one of the following calls: operator new(sizeof(T), 2, f) operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f)
  • new T[5] results in one of the following calls: operator new[](sizeof(T) * 5 + x) operator new[](sizeof(T) * 5 + x, std::align_val_t(alignof(T)))
  • new(2,f) T[5] results in one of the following calls: operator new[](sizeof(T) * 5 + x, 2, f) operator new[](sizeof(T) * 5 + x, std::align_val_t(alignof(T)), 2, f)
Here, each instance of x is a non-negative unspecified value representing array allocation overhead; the result of the new-expression will be offset by this amount from the value returned by operator new[].
This overhead may be applied in all array new-expressions, including those referencing a placement allocation function, except when referencing the library function operator new[](std​::​size_t, void*).
The amount of overhead may vary from one invocation of new to another.
— end example]
[Note 10: 
Unless an allocation function has a non-throwing exception specification, it indicates failure to allocate storage by throwing a std​::​bad_alloc exception ([basic.stc.dynamic.allocation], [except], [bad.alloc]); it returns a non-null pointer otherwise.
If the allocation function has a non-throwing exception specification, it returns null to indicate failure to allocate storage and a non-null pointer otherwise.
— end note]
If the allocation function is a non-allocating form ([new.delete.placement]) that returns null, the behavior is undefined.
Otherwise, if the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.
[Note 11: 
When the allocation function returns a value other than null, it must be a pointer to a block of storage in which space for the object has been reserved.
The block of storage is assumed to be appropriately aligned ([basic.align]) and of the requested size.
The address of the created object will not necessarily be the same as that of the block if the object is an array.
— end note]
A new-expression that creates an object of type T initializes that object as follows:
The invocation of the allocation function is sequenced before the evaluations of expressions in the new-initializer.
Initialization of the allocated object is sequenced before the value computation of the new-expression.
If the new-expression creates an array of objects of class type, the destructor is potentially invoked ([class.dtor]).
If any part of the object initialization described above61 terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed, after which the exception continues to propagate in the context of the new-expression.
If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed.
[Note 13: 
This is appropriate when the called allocation function does not allocate memory; otherwise, it is likely to result in a memory leak.
— end note]
If the new-expression does not begin with a unary ​::​ operator and the allocated type is a class type T or an array thereof, a search is performed for the deallocation function's name in the scope of T.
Otherwise, or if nothing is found, the deallocation function's name is looked up by searching for it in the global scope.
A declaration of a placement deallocation function matches the declaration of a placement allocation function if it has the same number of parameters and, after parameter transformations ([dcl.fct]), all parameter types except the first are identical.
If the lookup finds a single matching deallocation function, that function will be called; otherwise, no deallocation function will be called.
If the lookup finds a usual deallocation function and that function, considered as a placement deallocation function, would have been selected as a match for the allocation function, the program is ill-formed.
For a non-placement allocation function, the normal deallocation function lookup is used to find the matching deallocation function ([expr.delete]).
In any case, the matching deallocation function (if any) shall be non-deleted and accessible from the point where the new-expression appears.
[Example 7: struct S { // Placement allocation function: static void* operator new(std::size_t, std::size_t); // Usual (non-placement) deallocation function: static void operator delete(void*, std::size_t); }; S* p = new (0) S; // error: non-placement deallocation function matches // placement allocation function — end example]
If a new-expression calls a deallocation function, it passes the value returned from the allocation function call as the first argument of type void*.
If a placement deallocation function is called, it is passed the same additional arguments as were passed to the placement allocation function, that is, the same arguments as those specified with the new-placement syntax.
If the implementation is allowed to introduce a temporary object or make a copy of any argument as part of the call to the allocation function, it is unspecified whether the same object is used in the call to both the allocation and deallocation functions.
60)60)
If the conversion function returns a signed integer type, the second standard conversion converts to the unsigned type std​::​size_t and thus thwarts any attempt to detect a negative value afterwards.
61)61)
This can include evaluating a new-initializer and/or calling a constructor.

7.6.2.9 Delete [expr.delete]

The delete-expression operator destroys a most derived object or array created by a new-expression.
delete-expression:
:: delete cast-expression
:: delete [ ] cast-expression
The first alternative is a single-object delete expression, and the second is an array delete expression.
Whenever the delete keyword is immediately followed by empty square brackets, it shall be interpreted as the second alternative.62
If the operand is of class type, it is contextually implicitly converted ([conv]) to a pointer to object type and the converted operand is used in place of the original operand for the remainder of this subclause.
Otherwise, it shall be a prvalue of pointer to object type.
The delete-expression has type void.
In a single-object delete expression, the value of the operand of delete may be a null pointer value, a pointer value that resulted from a previous non-array new-expression, or a pointer to a base class subobject of an object created by such a new-expression.
If not, the behavior is undefined.
In an array delete expression, the value of the operand of delete may be a null pointer value or a pointer value that resulted from a previous array new-expression whose allocation function was not a non-allocating form ([new.delete.placement]).63
If not, the behavior is undefined.
[Note 1: 
This means that the syntax of the delete-expression must match the type of the object allocated by new, not the syntax of the new-expression.
— end note]
[Note 2: 
A pointer to a const type can be the operand of a delete-expression; it is not necessary to cast away the constness ([expr.const.cast]) of the pointer expression before it is used as the operand of the delete-expression.
— end note]
In a single-object delete expression, if the static type of the object to be deleted is not similar ([conv.qual]) to its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.
In an array delete expression, if the dynamic type of the object to be deleted is not similar to its static type, the behavior is undefined.
If the object being deleted has incomplete class type at the point of deletion, the program is ill-formed.
If the value of the operand of the delete-expression is not a null pointer value and the selected deallocation function (see below) is not a destroying operator delete, evaluating the delete-expression invokes the destructor (if any) for the object or the elements of the array being deleted.
The destructor shall be accessible from the point where the delete-expression appears.
In the case of an array, the elements are destroyed in order of decreasing address (that is, in reverse order of the completion of their constructor; see [class.base.init]).
If the value of the operand of the delete-expression is not a null pointer value, then:
[Note 3: 
The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception.
— end note]
If the value of the operand of the delete-expression is a null pointer value, it is unspecified whether a deallocation function will be called as described above.
If a deallocation function is called, it is operator delete for a single-object delete expression or operator delete[] for an array delete expression.
[Note 4: 
An implementation provides default definitions of the global deallocation functions ([new.delete.single], [new.delete.array]).
A C++ program can provide alternative definitions of these functions ([replacement.functions]), and/or class-specific versions ([class.free]).
— end note]
If the keyword delete in a delete-expression is not preceded by the unary ​::​ operator and the type of the operand is a pointer to a (possibly cv-qualified) class type T or (possibly multidimensional) array thereof:
  • For a single-object delete expression, if the operand is a pointer to cv T and T has a virtual destructor, the deallocation function is the one selected at the point of definition of the dynamic type's virtual destructor ([class.dtor]).
  • Otherwise, a search is performed for the deallocation function's name in the scope of T.
Otherwise, or if nothing is found, the deallocation function's name is looked up by searching for it in the global scope.
In any case, any declarations other than of usual deallocation functions ([basic.stc.dynamic.deallocation]) are discarded.
[Note 5: 
If only a placement deallocation function is found in a class, the program is ill-formed because the lookup set is empty ([basic.lookup]).
— end note]
The deallocation function to be called is selected as follows:
  • If any of the deallocation functions is a destroying operator delete, all deallocation functions that are not destroying operator deletes are eliminated from further consideration.
  • If the type has new-extended alignment, a function with a parameter of type std​::​align_val_t is preferred; otherwise a function without such a parameter is preferred.
    If any preferred functions are found, all non-preferred functions are eliminated from further consideration.
  • If exactly one function remains, that function is selected and the selection process terminates.
  • If the deallocation functions belong to a class scope, the one without a parameter of type std​::​size_t is selected.
  • If the type is complete and if, for an array delete expression only, the operand is a pointer to a class type with a non-trivial destructor or a (possibly multidimensional) array thereof, the function with a parameter of type std​::​size_t is selected.
  • Otherwise, it is unspecified whether a deallocation function with a parameter of type std​::​size_t is selected.
Unless the deallocation function is selected at the point of definition of the dynamic type's virtual destructor, the selected deallocation function shall be accessible from the point where the delete-expression appears.
For a single-object delete expression, the deleted object is the object A pointed to by the operand if the static type of A does not have a virtual destructor, and the most-derived object of A otherwise.
[Note 6: 
If the deallocation function is not a destroying operator delete and the deleted object is not the most derived object in the former case, the behavior is undefined, as stated above.
— end note]
For an array delete expression, the deleted object is the array object.
When a delete-expression is executed, the selected deallocation function shall be called with the address of the deleted object in a single-object delete expression, or the address of the deleted object suitably adjusted for the array allocation overhead ([expr.new]) in an array delete expression, as its first argument.
[Note 7: 
Any cv-qualifiers in the type of the deleted object are ignored when forming this argument.
— end note]
If a destroying operator delete is used, an unspecified value is passed as the argument corresponding to the parameter of type std​::​destroying_delete_t.
If a deallocation function with a parameter of type std​::​align_val_t is used, the alignment of the type of the deleted object is passed as the corresponding argument.
If a deallocation function with a parameter of type std​::​size_t is used, the size of the deleted object in a single-object delete expression, or of the array plus allocation overhead in an array delete expression, is passed as the corresponding argument.
[Note 8: 
If this results in a call to a replaceable deallocation function, and either the first argument was not the result of a prior call to a replaceable allocation function or the second or third argument was not the corresponding argument in said call, the behavior is undefined ([new.delete.single], [new.delete.array]).
— end note]
62)62)
A lambda-expression with a lambda-introducer that consists of empty square brackets can follow the delete keyword if the lambda-expression is enclosed in parentheses.
63)63)
For nonzero-length arrays, this is the same as a pointer to the first element of the array created by that new-expression.
Zero-length arrays do not have a first element.