5 Expressions [expr]

5.3 Unary expressions [expr.unary]

Expressions with unary operators group right-to-left.

unary-expression:
    postfix-expression
    ++ cast-expression
    -- cast-expression
    unary-operator cast-expression
    sizeof unary-expression
    sizeof ( type-id )
    sizeof ... ( identifier )
    alignof ( type-id )
    noexcept-expression
    new-expression
    delete-expression

unary-operator: one of
    *  &  +  -  !  ~

5.3.1 Unary operators [expr.unary.op]

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer to T,” the type of the result is “T.” [ Note: 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 ]

The result of each of the following unary operators is a prvalue.

The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m. Otherwise, if the type of the expression is T, the result has type “pointer to T” and is a prvalue that is the address of the designated object ([intro.memory]) or a pointer to the designated function. [ Note: In particular, the address of an object of type “cv T” is “pointer to cv T”, with the same cv-qualification.  — end note ] [ Example:

struct A { int i; };
struct B : A { };
... &B::i ...       // has type int A::*

 — end example ] [ Note: 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: 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.

The address of an overloaded function (Clause [over]) can be taken only in a context that uniquely determines which version of the overloaded function is referred to (see [over.over]). [ Note: since the context might determine 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 have 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 have arithmetic or unscoped enumeration type and the result is the negation 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 2n, where n is the number of bits in the promoted operand. The type of the result is the type of the promoted operand.

The operand of the logical negation operator ! is contextually converted to bool (Clause [conv]); its value is true if the converted operand is false and false otherwise. The type of the result is bool.

The operand of ~ shall have integral or unscoped enumeration type; the result is the one's complement of its operand. Integral promotions are performed. The type of the result is the type of the promoted operand. There is an ambiguity in the unary-expression ~X(), where X is a class-name or decltype-specifier. The ambiguity is resolved in favor of treating ~ as a unary complement rather than treating ~X as referring to a destructor.

5.3.2 Increment and decrement [expr.pre.incr]

The operand of prefix ++ is modified by adding 1, or set to true if it is bool (this use is deprecated). The operand shall be a modifiable lvalue. The type of the operand shall be an arithmetic type or a pointer to a completely-defined object type. The result is the updated operand; it is an lvalue, and it is a bit-field if the operand is a bit-field. If x is not of type bool, the expression ++x is equivalent to x+=1 Note: See the discussions of addition ([expr.add]) and assignment operators ([expr.ass]) for information on conversions.  — end note ]

The operand of prefix -- is modified by subtracting 1. The operand shall not be of type bool. The requirements on the operand of prefix -- and the properties of its result are otherwise the same as those of prefix ++. [ Note: For postfix increment and decrement, see [expr.post.incr].  — end note ]

5.3.3 Sizeof [expr.sizeof]

The sizeof operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is an unevaluated operand (Clause [expr]), or a parenthesized type-id. The sizeof operator shall not be applied to an expression that has function or incomplete type, to an enumeration type whose underlying type is not fixed before all its enumerators have been declared, to the parenthesized name of such types, or to a glvalue that designates a bit-field. sizeof(char), sizeof(signed char) and sizeof(unsigned char) are 1. The result of sizeof applied to any other fundamental type ([basic.fundamental]) is implementation-defined. [ Note: in particular, sizeof(bool), sizeof(char16_t), sizeof(char32_t), and sizeof(wchar_t) are implementation-defined.75  — end note ] [ Note: See [intro.memory] for the definition of byte and [basic.types] for the definition of object representation.  — end note ]

When applied to a reference or 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 size of a most derived class shall be greater than zero ([intro.object]). The result of applying sizeof to a base class subobject is the size of the base class type.76 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 sizeof operator can be applied to a pointer to a function, but shall not be applied directly to a function.

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.

The identifier in a sizeof... expression shall name a parameter pack. The sizeof... operator yields the number of arguments provided for the parameter pack identifier. A sizeof... expression is a pack expansion ([temp.variadic]). [ Example:

template<class... Types>
struct count {
  static const std::size_t value = sizeof...(Types);
};

 — end example ]

The result of sizeof and sizeof... is a constant of type std::size_t. [ Note: std::size_t is defined in the standard header <cstddef> ([support.types]).  — end note ]

sizeof(bool) is not required to be 1.

The actual size of a base class subobject may be less than the result of applying sizeof to the subobject, due to virtual base classes and less strict padding requirements on base class subobjects.

5.3.4 New [expr.new]

The new-expression attempts to create an object of the type-id ([dcl.name]) or new-type-id to which it is applied. The type of that object is the allocated type. This type shall be a complete object type, but not an abstract class type or array thereof ([intro.object], [basic.types], [class.abstract]). It is implementation-defined whether over-aligned types are supported ([basic.align]). [ Note: because references are not objects, references cannot be created by new-expressions.  — end note ] [ Note: the type-id may be a cv-qualified type, in which case the object created by the new-expression has a cv-qualified type.  — end note ]

new-expression:
    ::opt new new-placementopt new-type-id new-initializeropt 
    ::opt new new-placementopt ( type-id ) new-initializeropt

new-placement:
    ( expression-list )
new-type-id:
    type-specifier-seq new-declaratoropt
new-declarator:
    ptr-operator new-declaratoropt 
    noptr-new-declarator
noptr-new-declarator:
    [ expression ] attribute-specifier-seqopt
    noptr-new-declarator [ constant-expression ] attribute-specifier-seqopt
new-initializer:
    ( expression-listopt )
    braced-init-list

Entities created by a new-expression have dynamic storage duration ([basic.stc.dynamic]). [ Note: the lifetime of such an entity is not necessarily restricted to the scope in which it is created.  — end note ] If the entity is a non-array object, the new-expression returns a pointer to the object created. If it is an array, the new-expression returns a pointer to the initial element of the array.

If the auto type-specifier appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the new-expression shall contain a new-initializer of the form

( assignment-expression )

The allocated type is deduced from the new-initializer as follows: Let e be the assignment-expression in the new-initializer 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(e);

Example:

new auto(1);                    // allocated type is int
auto x = new auto('a');         // allocated type is char, x is of type char*

 — end example ]

The new-type-id in a new-expression is the longest possible sequence of new-declarators. [ Note: this prevents ambiguities between the declarator operators &, &&, *, and [] and their expression counterparts.  — end note ] [ Example:

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: parentheses in a new-type-id of a new-expression can have surprising effects. [ Example:

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 ]

When the allocated object is an array (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 pointer to the initial element (if any) of the array. [ Note: both new int and new int[10] have type int* and the type of new int[i][10] is int (*)[10]  — 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 shall evaluate to a strictly positive value. The expression in a noptr-new-declaratoris implicitly converted to std::size_t. [ Example: 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 ]

The expression in a noptr-new-declarator is erroneous 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])77 is less than zero;

  • its value is such that the size of the allocated object would exceed the implementation-defined limit (annex [implimits]); 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 expression, after converting to std::size_t, is a core constant expression and the expression is erroneous, the program is ill-formed. Otherwise, a new-expression with an erroneous expression does not call an allocation function and terminates by throwing an exception of a type that would match a handler ([except.handle]) of type std::bad_array_new_length ([new.badlength]). When the value of the expression is zero, the allocation function is called to allocate an array with no elements.

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 ([basic.stc.dynamic.deallocation]). 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: an implementation shall 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]).  — end note ]

If the new-expression begins with a unary :: operator, the allocation function's name is looked up in the global scope. Otherwise, if the allocated type is a class type T or array thereof, the allocation function's name is looked up in the scope of T. If this lookup fails to find the name, or if the allocated type is not a class type, the allocation function's name is looked up 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. 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:

  void mergeable(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 unmergeable(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. For arrays of char and unsigned char, 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 ([basic.align]) of any object type whose size is no greater than the size of the array being created. [ Note: 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. If used, overload resolution is performed on a function call created by assembling an argument list consisting of the amount of space requested (the first argument) and the expressions in the new-placement part of the new-expression (the second and succeeding arguments). The first of these arguments has type std::size_t and the remaining arguments have the corresponding types of the expressions in the new-placement.

Example:

  • new T results in a call of operator new(sizeof(T)),

  • new(2,f) T results in a call of operator new(sizeof(T),2,f),

  • new T[5] results in a call of operator new[](sizeof(T)*5+x), and

  • new(2,f) T[5] results in a call of operator new[](sizeof(T)*5+y,2,f).

Here, x and y are non-negative unspecified values 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 the library function operator new[](std::size_t, void*) and other placement allocation functions. The amount of overhead may vary from one invocation of new to another.  — end example ]

Note: unless an allocation function is declared with a non-throwing exception-specification ([except.spec]), it indicates failure to allocate storage by throwing a std::bad_alloc exception (Clause [except], [bad.alloc]); it returns a non-null pointer otherwise. If the allocation function is declared with 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 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: 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 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:

  • If the new-initializer is omitted, the object is default-initialized ([dcl.init]). [ Note: If no initialization is performed, the object has an indeterminate value.  — end note ]

  • Otherwise, the new-initializer is interpreted according to the initialization rules of [dcl.init] for direct-initialization.

The invocation of the allocation function is indeterminately sequenced with respect to the evaluations of expressions in the new-initializer. Initialization of the allocated object is sequenced before the value computation of the new-expression. It is unspecified whether expressions in the new-initializer are evaluated if the allocation function returns the null pointer or exits using an exception.

If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function ([class.free]), and the constructor ([class.ctor]). 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 above78 terminates by throwing an exception, storage has been obtained for the object, 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: 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 begins with a unary :: operator, the deallocation function's name is looked up in the global scope. Otherwise, if the allocated type is a class type T or an array thereof, the deallocation function's name is looked up in the scope of T. If this lookup fails to find the name, or if the allocated type is not a class type or array thereof, the deallocation function's name is looked up 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 the two-parameter form of a usual deallocation function ([basic.stc.dynamic.deallocation]) 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]) [ Example:

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;   // ill-formed: 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 make a copy of any argument as part of the call to the allocation function, it is allowed to make a copy (of the same original value) as part of the call to the deallocation function or to reuse the copy made as part of the call to the allocation function. If the copy is elided in one place, it need not be elided in the other.

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.

This may include evaluating a new-initializer and/or calling a constructor.

5.3.5 Delete [expr.delete]

The delete-expression operator destroys a most derived object ([intro.object]) or array created by a new-expression.

delete-expression:
    ::opt delete cast-expression
    ::opt delete [ ] cast-expression

The first alternative is for non-array objects, and the second is for arrays. Whenever the delete keyword is immediately followed by empty square brackets, it shall be interpreted as the second alternative.79 The operand shall be of pointer to object type or of class type. If of class type, the operand is contextually implicitly converted (Clause [conv]) to a pointer to object type.80 The delete-expression's result has type void.

If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this section. In the first alternative (delete object), the value of the operand of delete may be a null pointer value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject ([intro.object]) representing a base class of such an object (Clause [class.derived]). If not, the behavior is undefined. In the second alternative (delete array), 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.81 If not, the behavior is undefined. [ Note: 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: 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 the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, 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 the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.

The cast-expression in a delete-expression shall be evaluated exactly once.

If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.

If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted. In the case of an array, the elements will be 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:

Otherwise, it is unspecified whether the deallocation function will be called. [ Note: The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception.  — end note ]

Note: An implementation provides default definitions of the global deallocation functions operator delete() for non-arrays ([new.delete.single]) and operator delete[]() for arrays ([new.delete.array]). A C++ program can provide alternative definitions of these functions ([replacement.functions]), and/or class-specific versions ([class.free]).  — end note ]

When the keyword delete in a delete-expression is preceded by the unary :: operator, the deallocation function's name is looked up in global scope. Otherwise, the lookup considers class-specific deallocation functions ([class.free]). If no class-specific deallocation function is found, the deallocation function's name is looked up in global scope.

If the type is complete and if deallocation function lookup finds both a usual deallocation function with only a pointer parameter and a usual deallocation function with both a pointer parameter and a size parameter, then the selected deallocation function shall be the one with two parameters. Otherwise, the selected deallocation function shall be the function with one parameter.

When a delete-expression is executed, the selected deallocation function shall be called with the address of the block of storage to be reclaimed as its first argument and (if the two-parameter deallocation function is used) the size of the block as its second argument.82

Access and ambiguity control are done for both the deallocation function and the destructor ([class.dtor], [class.free]).

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.

This implies that an object cannot be deleted using a pointer of type void* because void is not an object type.

For non-zero-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.

If the static type of the object to be deleted is complete and is different from the dynamic type, and the destructor is not virtual, the size might be incorrect, but that case is already undefined, as stated above.

5.3.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 an integral constant of type std::size_t.

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.

5.3.7 noexcept operator [expr.unary.noexcept]

The noexcept operator determines whether the evaluation of its operand, which is an unevaluated operand (Clause [expr]), can throw an exception ([except.throw]).

noexcept-expression:
  noexcept ( expression )

The result of the noexcept operator is a constant of type bool and is a prvalue.

The result of the noexcept operator is false if in a potentially-evaluated context the expression would contain

Otherwise, the result is true.

This includes implicit calls such as the call to an allocation function in a new-expression.