[expr.compound] (original) (raw)
7 Expressions [expr]
7.6 Compound expressions [expr.compound]
7.6.1 Postfix expressions [expr.post]
7.6.1.1 General [expr.post.general]
Postfix expressions group left-to-right.
[Note 1:
The > token following thetype-id in a dynamic_cast,static_cast, reinterpret_cast, orconst_cast can be the product of replacing a>> token by two consecutive >tokens ([temp.names]).
— _end note_]
7.6.1.2 Subscripting [expr.sub]
A subscript expression is a postfix expression followed by square brackets containing a possibly empty, comma-separated list of initializer-clause_s_that constitute the arguments to the subscript operator.
The postfix-expression and the initialization of the object parameter ([dcl.fct]) of any applicable subscript operator function ([over.sub]) is sequenced before each expression in the expression-list and also before any default argument ([dcl.fct.default]).
The initialization of a non-object parameter of a subscript operator function S, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other non-object parameter of S.
With the built-in subscript operator, an expression-list shall be present, consisting of a single assignment-expression.
One of the expressions shall be a glvalue of type “array ofT” or a prvalue of type “pointer to T” and the other shall be a prvalue of unscoped enumeration or integral type.
The result is of type “T”.
The type “T” shall be a completely-defined object type.48
The expression E1[E2] is identical (by definition) to*((E1)+(E2)), except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise.
[Note 1:
Despite its asymmetric appearance, subscripting is a commutative operation except for sequencing.
See [expr.unary] and [expr.add] for details of * and+ and [dcl.array] for details of array types.
— _end note_]
7.6.1.3 Function call [expr.call]
A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list ofinitializer-clauses which constitute the arguments to the function.
[Note 1:
If the postfix expression is a function or member function name, the appropriate function and the validity of the call are determined according to the rules in [over.match].
— _end note_]
The postfix expression shall have function type or function pointer type.
For a call to a non-member function or to a static member function, the postfix expression shall be either an lvalue that refers to a function (in which case the function-to-pointer standard conversion ([conv.func]) is suppressed on the postfix expression), or a prvalue of function pointer type.
If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called.
Otherwise, its final overrider in the dynamic type of the object expression is called; such a call is referred to as avirtual function call.
[Note 2:
The dynamic type is the type of the object referred to by the current value of the object expression.
[class.cdtor] describes the behavior of virtual function calls when the object expression refers to an object under construction or destruction.
— _end note_]
[Note 3:
If a function or member function name is used, and name lookup does not find a declaration of that name, the program is ill-formed.
No function is implicitly declared by such a call.
— _end note_]
If the postfix-expression names a destructor or pseudo-destructor ([expr.prim.id.dtor]), the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different.
If the postfix-expression names a pseudo-destructor (in which case the postfix-expressionis a possibly-parenthesized class member access), the function call destroys the object of scalar type denoted by the object expression of the class member access ([expr.ref], [basic.life]).
A type iscall-compatible with a function type if is the same type as or if the type “pointer to ” can be converted to type “pointer to ” via a function pointer conversion ([conv.fctptr]).
Calling a function through an expression whose function type is not call-compatible with the type of the called function's definition results in undefined behavior.
[Note 4:
This requirement allows the case when the expression has the type of a potentially-throwing function, but the called function has a non-throwing exception specification, and the function types are otherwise the same.
— _end note_]
When a function is called, each parameter ([dcl.fct]) is initialized ([dcl.init], [class.copy.ctor]) with its corresponding argument, and each precondition assertion of the function is evaluated.
([dcl.contract.func]) If the function is an explicit object member function and there is an implied object argument ([over.call.func]), the list of provided arguments is preceded by the implied object argument for the purposes of this correspondence.
If there is no corresponding argument, the default argument for the parameter is used.
[Example 1: template<typename ...T> int f(int n = 0, T ...t);int x = f<int>(); — _end example_]
If the function is an implicit object member function, the object expression of the class member access shall be a glvalue and the implicit object parameter of the function ([over.match.funcs]) is initialized with that glvalue, converted as if by an explicit type conversion.
[Note 5:
There is no access or ambiguity checking on this conversion; the access checking and disambiguation are done as part of the (possibly implicit) class member access operator.
— _end note_]
When a function is called, the type of any parameter shall not be a class type that is either incomplete or abstract.
[Note 6:
This still allows a parameter to be a pointer or reference to such a type.
However, it prevents a passed-by-value parameter to have an incomplete or abstract class type.
— _end note_]
It is implementation-defined whether a parameter is destroyed when the function in which it is defined exits ([stmt.return], [except.ctor], [expr.await]) or at the end of the enclosing full-expression; parameters are always destroyed in the reverse order of their construction.
The initialization and destruction of each parameter occurs within the context of the full-expression ([intro.execution]) where the function call appears.
[Example 2:
The access ([class.access.general]) of the constructor, conversion functions, or destructor is checked at the point of call.
If a constructor or destructor for a function parameter throws an exception, any function-try-block ([except.pre]) of the called function with a handler that can handle the exception is not considered.
— _end example_]
The postfix-expression is sequenced before each expression in the expression-listand any default argument.
The initialization of a parameter or, if the implementation introduces any temporary objects to hold the values of function parameters ([class.temporary]), the initialization of those temporaries, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.
These evaluations are sequenced before the evaluation of the precondition assertions of the function, which are evaluated in sequence ([dcl.contract.func]).
For any temporaries introduced to hold the values of function parameters, the initialization of the parameter objects from those temporaries is indeterminately sequenced with respect to the evaluation of each precondition assertion.
[Note 7:
All side effects of argument evaluations are sequenced before the function is entered (see [intro.execution]).
— _end note_]
[Example 3: void f() { std::string s = "but I have heard it works even if you don't believe in it"; s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, ""); assert(s == "I have heard it works only if you believe in it"); } — _end example_]
[Note 8:
If an operator function is invoked using operator notation, argument evaluation is sequenced as specified for the built-in operator; see [over.match.oper].
— _end note_]
[Example 4: struct S { S(int);};int operator<<(S, int);int i, j;int x = S(i=1) << (i=2);int y = operator<<(S(j=1), j=2);
After performing the initializations, the value of i is 2 (see [expr.shift]), but it is unspecified whether the value of j is 1 or 2.
— _end example_]
The result of a function call is the result of the possibly-converted operand of the return statement ([stmt.return]) that transferred control out of the called function (if any), except in a virtual function call if the return type of the final overrider is different from the return type of the statically chosen function, the value returned from the final overrider is converted to the return type of the statically chosen function.
When the called function exits normally ([stmt.return], [expr.await]), all postcondition assertions of the function are evaluated in sequence ([dcl.contract.func]).
If the implementation introduces any temporary objects to hold the result value as specified in [class.temporary], the evaluation of each postcondition assertion is indeterminately sequenced with respect to the initialization of any of those temporaries or the result object.
These evaluations, in turn, are sequenced before the destruction of any function parameters.
[Note 9:
A function can change the values of its non-const parameters, but these changes cannot affect the values of the arguments except where a parameter is of a reference type ([dcl.ref]); if the reference is to a const-qualified type, const_cast needs to be used to cast away the constness in order to modify the argument's value.
In addition, it is possible to modify the values of non-constant objects through pointer parameters.
— _end note_]
A function can be declared to accept fewer arguments (by declaring default arguments) or more arguments (by using the ellipsis,..., or a function parameter pack ([dcl.fct])) than the number of parameters in the function definition.
[Note 10:
This implies that, except where the ellipsis (...) or a function parameter pack is used, a parameter is available for each argument.
— _end note_]
When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg ([support.runtime]).
[Note 11:
This paragraph does not apply to arguments passed to a function parameter pack.
Function parameter packs are expanded during template instantiation ([temp.variadic]), thus each such argument has a corresponding parameter when a function template specialization is actually called.
— _end note_]
An argument that has type cv std::nullptr_t is converted to type void* ([conv.ptr]).
After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer-to-member, or class type, the program is ill-formed.
Passing a potentially-evaluated argument of a scoped enumeration type ([dcl.enum]) or of a class type ([class]) having an eligible non-trivial copy constructor ([special], [class.copy.ctor]), an eligible non-trivial move constructor, or a non-trivial destructor ([class.dtor]), with no corresponding parameter, is conditionally-supported withimplementation-defined semantics.
If the argument has integral or enumeration type that is subject to the integral promotions, or a floating-point type that is subject to thefloating-point promotion, the value of the argument is converted to the promoted type before the call.
These promotions are referred to as the default argument promotions.
A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
If it is a non-void prvalue, the type of the function call expression shall be complete, except as specified in [dcl.type.decltype].
7.6.1.4 Explicit type conversion (functional notation) [expr.type.conv]
If the type is a placeholder for a deduced class type, it is replaced by the return type of the function selected by overload resolution for class template deductionfor the remainder of this subclause.
Otherwise, if the type contains a placeholder type, it is replaced by the type determined by placeholder type deduction ([dcl.type.auto.deduct]).
Let T denote the resulting type.
Then:
- If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression ([expr.cast]).
- Otherwise, if T is cv void, the initializer shall be () or {}(after pack expansion, if any), and the expression is a prvalue of type voidthat performs no initialization.
- Otherwise, if T is a reference type, the expression has the same effect as direct-initializing an invented variable t of type T from the initializer and then using t as the result of the expression; the result is an lvalue ifT is an lvalue reference type or an rvalue reference to function type and an xvalue otherwise.
- Otherwise, the expression is a prvalue of type Twhose result object is direct-initialized ([dcl.init]) with the initializer.
If the initializer is a parenthesized optional expression-list,T shall not be an array type.
[Example 1: struct A {};void f(A&); void f(A&&); A& g();void h() { f(g()); f(A(g())); f(auto(g())); } — _end example_]
7.6.1.5 Class member access [expr.ref]
A postfix expression followed by a dot . or an arrow ->, optionally followed by the keywordtemplate, and then followed by anid-expression, is a postfix expression.
[Note 1:
If the keyword template is used, the following unqualified name is considered to refer to a template ([temp.names]).
— _end note_]
For the first option (dot), if the id-expression names a static member or an enumerator, the first expression is a discarded-value expression ([expr.context]); if the id-expression names a non-static data member, the first expression shall be a glvalue.
For the second option (arrow), the first expression shall be a prvalue having pointer type.
The expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder of[expr.ref] will address only the first option (dot).49
The postfix expression before the dot is evaluated;50the result of that evaluation, together with the id-expression, determines the result of the entire postfix expression.
If the object expression is of scalar type,E2 shall name the pseudo-destructor of that same type (ignoring cv-qualifications) andE1.E2 is a prvalue of type “function of () returning void”.
[Note 2:
This value can only be used for a notional function call ([expr.prim.id.dtor]).
— _end note_]
Otherwise, the object expression shall be of class type.
The class type shall be complete unless the class member access appears in the definition of that class.
[Note 3:
The program is ill-formed if the result differs from that when the class is complete ([class.member.lookup]).
— _end note_]
[Note 4:
[basic.lookup.qual] describes how names are looked up after the. and -> operators.
— _end note_]
If E2 is a bit-field, E1.E2 is a bit-field.
The type and value category of E1.E2 are determined as follows.
In the remainder of [expr.ref], cq represents eitherconst or the absence of const and vq represents either volatile or the absence of volatile.
If E2 is declared to have type “reference to T”, thenE1.E2 is an lvalue of type T.
If E2 is a static data member,E1.E2 designates the object or function to which the reference is bound, otherwise E1.E2 designates the object or function to which the corresponding reference member of E1 is bound.
Otherwise, one of the following rules applies.
- If E2 is a static data member and the type of E2is T, then E1.E2 is an lvalue; the expression designates the named member of the class.
The type of E1.E2 is T. - If E2 is a non-static data member and the type ofE1 is “cq1 vq1 X”, and the type of E2is “cq2 vq2 T”, the expression designates the corresponding member subobject of the object designated by the first expression.
If E1is an lvalue, then E1.E2 is an lvalue; otherwise E1.E2 is an xvalue.
Let the notation vq12 stand for the “union” of_vq1_ and vq2; that is, if vq1 or vq2_is volatile, then vq12 is volatile.
Similarly, let the notation cq12 stand for the “union” of cq1_and cq2; that is, if cq1 or cq2 isconst, then cq12 is const.
If E2 is declared to be a mutable member, then the type of E1.E2is “_vq12 T”.
If E2 is not declared to be amutable member, then the type of E1.E2 is “_cq12 vq12 T”. - If E2 is an overload set, the expression shall be the (possibly-parenthesized) left-hand operand of a member function call ([expr.call]), and function overload resolution ([over.match]) is used to select the function to which E2 refers.
The type of E1.E2 is the type of E2and E1.E2 refers to the function referred to by E2.- If E2 refers to a static member function,E1.E2 is an lvalue.
- Otherwise (when E2 refers to a non-static member function),E1.E2 is a prvalue.
[Note 5:
Any redundant set of parentheses surrounding the expression is ignored ([expr.prim.paren]).
— _end note_]
- If E2 is a nested type, the expression E1.E2 is ill-formed.
- If E2 is a member enumerator and the type of E2is T, the expression E1.E2 is a prvalue of type Twhose value is the value of the enumerator.
If E2 is a non-static member, the program is ill-formed if the class of which E2 is directly a member is an ambiguous base ([class.member.lookup]) of the naming class ([class.access.base]) of E2.
[Note 6:
The program is also ill-formed if the naming class is an ambiguous base of the class type of the object expression; see [class.access.base].
— _end note_]
If E2 is a non-static member and the result of E1 is an object whose type is not similar ([conv.qual]) to the type of E1, the behavior is undefined.
[Example 1: struct A { int i; };struct B { int j; };struct D : A, B {};void f() { D d;static_cast<B&>(d).j; reinterpret_cast<B&>(d).j; } — _end example_]
7.6.1.6 Increment and decrement [expr.post.incr]
The value of a postfix ++ expression is the value obtained by applying the lvalue-to-rvalue conversion ([conv.lval]) to its operand.
[Note 1:
The value obtained is a copy of the original value.
— _end note_]
The operand shall be a modifiable lvalue.
The type of the operand shall be an arithmetic type other than cv bool, or a pointer to a complete object type.
An operand with volatile-qualified type is deprecated; see [depr.volatile.type].
The value of the operand object is modified ([defns.access]) as if it were the operand of the prefix ++ operator ([expr.pre.incr]).
Thevalue computation of the ++ expression is sequenced before the modification of the operand object.
With respect to an indeterminately-sequenced function call, the operation of postfix++ is a single evaluation.
[Note 2:
Therefore, a function call cannot intervene between the lvalue-to-rvalue conversion and the side effect associated with any single postfix ++ operator.
— _end note_]
The result is a prvalue.
The type of the result is the cv-unqualified version of the type of the operand.
The operand of postfix -- is decremented analogously to the postfix ++ operator.
[Note 3:
For prefix increment and decrement, see [expr.pre.incr].
— _end note_]
7.6.1.7 Dynamic cast [expr.dynamic.cast]
The result of the expression dynamic_cast<T>(v) is the result of converting the expression v to type T.
T shall be a pointer or reference to a complete class type, or “pointer to cv void”.
The dynamic_cast operator shall not cast away constness ([expr.const.cast]).
If T is a pointer type, v shall be a prvalue of a pointer to complete class type, and the result is a prvalue of typeT.
If T is an lvalue reference type, v shall be an lvalue of a complete class type, and the result is an lvalue of the type referred to by T.
If T is an rvalue reference type,v shall be a glvalue having a complete class type, and the result is an xvalue of the type referred to by T.
If the type of v is the same as T (ignoring cv-qualifications), the result isv (converted if necessary).
If T is “pointer to cv1 B” and v has type “pointer to cv2 D” such that B is a base class of D, the result is a pointer to the unique Bsubobject of the D object pointed to by v, or a null pointer value if v is a null pointer value.
Similarly, ifT is “reference to cv1 B” and v has type cv2 D such that B is a base class ofD, the result is the unique B subobject of the Dobject referred to by v.51
In both the pointer and reference cases, the program is ill-formed if B is an inaccessible or ambiguous base class of D.
[Example 1: struct B { };struct D : B { };void foo(D* dp) { B* bp = dynamic_cast<B*>(dp); } — _end example_]
If v is a null pointer value, the result is a null pointer value.
If v has type “pointer to cv U” andv does not point to an object whose type is similar ([conv.qual]) to U and that is within its lifetime or within its period of construction or destruction ([class.cdtor]), the behavior is undefined.
If v is a glvalue of type U andv does not refer to an object whose type is similar to U and that is within its lifetime or within its period of construction or destruction, the behavior is undefined.
If T is “pointer to cv void”, then the result is a pointer to the most derived object pointed to by v.
Otherwise, a runtime check is applied to see if the object pointed or referred to by v can be converted to the type pointed or referred to by T.
Let C be the class type to which T points or refers.
The runtime check logically executes as follows:
- If, in the most derived object pointed (referred) to by v,v points (refers) to a public base class subobject of aC object, and if only one object of type C is derived from the subobject pointed (referred) to by v, the result points (refers) to that C object.
- Otherwise, if v points (refers) to a public base class subobject of the most derived object, and the type of the most derived object has a base class, of type C, that is unambiguous and public, the result points (refers) to theC subobject of the most derived object.
- Otherwise, the runtime check fails.
The value of a failed cast to pointer type is the null pointer value of the required result type.
[Example 2: class A { virtual void f(); };class B { virtual void g(); };class D : public virtual A, private B { };void g() { D d; B* bp = (B*)&d; A* ap = &d; D& dr = dynamic_cast<D&>(*bp); ap = dynamic_cast<A*>(bp); bp = dynamic_cast<B*>(ap); ap = dynamic_cast<A*>(&d); bp = dynamic_cast<B*>(&d); } class E : public D, public B { };class F : public E, public D { };void h() { F f; A* ap = &f; D* dp = dynamic_cast<D*>(ap); E* ep = (E*)ap; E* ep1 = dynamic_cast<E*>(ap); } — _end example_]
[Note 1:
Subclause [class.cdtor] describes the behavior of a dynamic_castapplied to an object under construction or destruction.
— _end note_]
7.6.1.8 Type identification [expr.typeid]
The result of a typeid expression is an lvalue of static typeconst std::type_info ([type.info]) and dynamic type const std::type_info or const name where name is animplementation-defined class publicly derived fromstd::type_info which preserves the behavior described in [type.info].52
The lifetime of the object referred to by the lvalue extends to the end of the program.
Whether or not the destructor is called for thestd::type_info object at the end of the program is unspecified.
If the type of the expression or type-id operand is a (possibly cv-qualified) class type or a reference to (possibly cv-qualified) class type, that class shall be completely defined.
If an expression operand of typeid is a possibly-parenthesized unary-expressionwhose unary-operator is * and whose operand evaluates to a null pointer value ([basic.compound]), the typeid expression throws an exception ([except.throw]) of a type that would match a handler of typestd::bad_typeid ([bad.typeid]).
[Note 1:
In other contexts, evaluating such a unary-expressionresults in undefined behavior ([expr.unary.op]).
— _end note_]
When typeid is applied to a glvalue whose type is a polymorphic class type ([class.virtual]), the result refers to astd::type_info object representing the type of the most derived object ([intro.object]) (that is, the dynamic type) to which the glvalue refers.
When typeid is applied to an expression other than a glvalue of a polymorphic class type, the result refers to a std::type_infoobject representing the static type of the expression.
When typeid is applied to a type-id, the result refers to a std::type_info object representing the type of thetype-id.
If the type of the type-id is a reference to a possibly cv-qualified type, the result of thetypeid expression refers to a std::type_info object representing the cv-unqualified referenced type.
If the type of the expression or type-id is a cv-qualified type, the result of the typeid expression refers to a std::type_info object representing the cv-unqualified type.
[Example 1: class D { }; D d1;const D d2;typeid(d1) == typeid(d2); typeid(D) == typeid(const D); typeid(D) == typeid(d2); typeid(D) == typeid(const D&); — _end example_]
[Note 3:
Subclause [class.cdtor] describes the behavior of typeidapplied to an object under construction or destruction.
— _end note_]
7.6.1.9 Static cast [expr.static.cast]
The result of the expression static_cast<T>(v) is the result of converting the expression v to type T.
If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue.
The static_cast operator shall not cast away constness ([expr.const.cast]).
An lvalue of type “cv1 B”, where B is a class type, can be cast to type “reference to cv2 D”, whereD is a complete class derived ([class.derived]) from B, if cv2 is the same cv-qualification as, or greater cv-qualification than,cv1.
If B is a virtual base class of Dor a base class of a virtual base class of D, or if no valid standard conversion from “pointer to D” to “pointer to B” exists ([conv.ptr]), the program is ill-formed.
An xvalue of type “cv1 B” can be cast to type “rvalue reference to_cv2_ D” with the same constraints as for an lvalue of type “cv1 B”.
If the object of type “cv1 B” is actually a base class subobject of an object of type D, the result refers to the enclosing object of typeD.
Otherwise, the behavior is undefined.
[Example 1: struct B { };struct D : public B { }; D d; B &br = d;static_cast<D&>(br); — _end example_]
An lvalue of type T1 can be cast to type “rvalue reference to T2” if T2 is reference-compatible withT1 ([dcl.init.ref]).
If the value is not a bit-field, the result refers to the object or the specified base class subobject thereof; otherwise, the lvalue-to-rvalue conversionis applied to the bit-field and the resulting prvalue is used as the operand of the static_cast for the remainder of this subclause.
If T2 is an inaccessible ([class.access]) or ambiguous ([class.member.lookup]) base class of T1, a program that necessitates such a cast is ill-formed.
Any expression can be explicitly converted to type cv void, in which case the operand is a discarded-value expression ([expr.prop]).
[Note 1:
Such a static_cast has no result as it is a prvalue of type void; see [basic.lval].
— _end note_]
[Note 2:
However, if the value is in a temporary object ([class.temporary]), the destructor for that object is not executed until the usual time, and the value of the object is preserved for the purpose of executing the destructor.
— _end note_]
Otherwise, an expression E can be explicitly converted to a type Tif there is an implicit conversion sequence ([over.best.ics]) from E to T, if overload resolution for a direct-initialization ([dcl.init]) of an object or reference of type T from Ewould find at least one viable function ([over.match.viable]), or if T is an aggregate type ([dcl.init.aggr]) having a first element x and there is an implicit conversion sequence from E to the type of x.
If T is a reference type, the effect is the same as performing the declaration and initializationT t(E);for some invented temporary variable t ([dcl.init]) and then using the temporary variable as the result of the conversion.
Otherwise, the result object is direct-initialized from E.
[Note 3:
The conversion is ill-formed when attempting to convert an expression of class type to an inaccessible or ambiguous base class.
— _end note_]
[Note 4:
If T is “array of unknown bound of U”, this direct-initialization defines the type of the expression as U[1].
— _end note_]
Otherwise, the inverse of a standard conversion sequence ([conv]) not containing an lvalue-to-rvalue ([conv.lval]), array-to-pointer ([conv.array]), function-to-pointer ([conv.func]), null pointer ([conv.ptr]), null member pointer ([conv.mem]), boolean ([conv.bool]), or function pointer ([conv.fctptr]) conversion, can be performed explicitly using static_cast.
A program is ill-formed if it uses static_cast to perform the inverse of an ill-formed standard conversion sequence.
[Example 2: struct B { };struct D : private B { };void f() { static_cast<D*>((B*)0); static_cast<int B::*>((int D::*)0); } — _end example_]
The lvalue-to-rvalue ([conv.lval]), array-to-pointer ([conv.array]), and function-to-pointer ([conv.func]) conversions are applied to the operand.
Such a static_cast is subject to the restriction that the explicit conversion does not cast away constness ([expr.const.cast]), and the following additional rules for specific cases:
A value of a scoped enumeration type ([dcl.enum]) can be explicitly converted to an integral type; the result is the same as that of converting to the enumeration's underlying type and then to the destination type.
A value of a scoped enumeration type can also be explicitly converted to a floating-point type; the result is the same as that of converting from the original value to the floating-point type.
A value of integral or enumeration type can be explicitly converted to a complete enumeration type.
If the enumeration type has a fixed underlying type, the value is first converted to that type by integral promotion ([conv.prom]) or integral conversion ([conv.integral]), if necessary, and then to the enumeration type.
If the enumeration type does not have a fixed underlying type, the value is unchanged if the original value is within the range of the enumeration values ([dcl.enum]), and otherwise, the behavior is undefined.
A value of floating-point type can also be explicitly converted to an enumeration type.
The resulting value is the same as converting the original value to the underlying type of the enumeration ([conv.fpint]), and subsequently to the enumeration type.
A prvalue of floating-point type can be explicitly converted to any other floating-point type.
If the source value can be exactly represented in the destination type, the result of the conversion has that exact representation.
If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values.
Otherwise, the behavior is undefined.
A prvalue of type “pointer to cv1 B”, where Bis a class type, can be converted to a prvalue of type “pointer to_cv2_ D”, where D is a complete classderivedfrom B, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.
If B is a virtual base class of D or a base class of a virtual base class of D, or if no valid standard conversion from “pointer to D” to “pointer to B” exists ([conv.ptr]), the program is ill-formed.
The null pointer value ([basic.compound]) is converted to the null pointer value of the destination type.
If the prvalue of type “pointer to cv1 B” points to a B that is actually a base class subobject of an object of type D, the resulting pointer points to the enclosing object of type D.
Otherwise, the behavior is undefined.
A prvalue of type “pointer to member of D of type cv1 T” can be converted to a prvalue of type “pointer to member ofB of type cv2 T”, whereD is a complete class type andB is a base class of D, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.
[Note 5:
Function types (including those used in pointer-to-member-function types) are never cv-qualified ([dcl.fct]).
— _end note_]
If no valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists ([conv.mem]), the program is ill-formed.
If class Bcontains the original member, or is a base class of the class containing the original member, the resulting pointer to member points to the original member.
Otherwise, the behavior is undefined.
[Note 6:
Although class B need not contain the original member, the dynamic type of the object with which indirection through the pointer to member is performed must contain the original member; see [expr.mptr.oper].
— _end note_]
A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”, where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.
If the original pointer value represents the addressA of a byte in memory andA does not satisfy the alignment requirement of T, then the resulting pointer value ([basic.compound]) is unspecified.
Otherwise, if the original pointer value points to an object a, and there is an object b of type similar to Tthat is pointer-interconvertible with a, the result is a pointer to b.
Otherwise, the pointer value is unchanged by the conversion.
[Example 3: T* p1 = new T;const T* p2 = static_cast<const T*>(static_cast<void*>(p1));bool b = p1 == p2; — _end example_]
No other conversion can be performed using static_cast.
7.6.1.10 Reinterpret cast [expr.reinterpret.cast]
The result of the expression reinterpret_cast<T>(v) is the result of converting the expression v to type T.
If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and thelvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions are performed on the expression v.
Conversions that can be performed explicitly using reinterpret_cast are listed below.
No other conversion can be performed explicitly using reinterpret_cast.
The reinterpret_cast operator shall not cast away constness ([expr.const.cast]).
An expression of integral, enumeration, pointer, or pointer-to-member type can be explicitly converted to its own type; such a cast yields the value of its operand.
[Note 1:
The mapping performed by reinterpret_cast might, or might not, produce a representation different from the original value.
— _end note_]
A pointer can be explicitly converted to any integral type large enough to hold all values of its type.
The mapping function is implementation-defined.
[Note 2:
It is intended to be unsurprising to those who know the addressing structure of the underlying machine.
— _end note_]
A value of type std::nullptr_t can be converted to an integral type; the conversion has the same meaning and validity as a conversion of(void*)0 to the integral type.
[Note 3:
A reinterpret_castcannot be used to convert a value of any type to the typestd::nullptr_t.
— _end note_]
A value of integral type or enumeration type can be explicitly converted to a pointer.
A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value ([basic.compound]);mappings between pointers and integers are otherwiseimplementation-defined.
A function pointer can be explicitly converted to a function pointer of a different type.
[Note 4:
The effect of calling a function through a pointer to a function type ([dcl.fct]) that is not the same as the type used in the definition of the function is undefined ([expr.call]).
— _end note_]
Except that converting a prvalue of type “pointer to T1” to the type “pointer toT2” (where T1 and T2 are function types) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.
An object pointer can be explicitly converted to an object pointer of a different type.53
When a prvalue v of object pointer type is converted to the object pointer type “pointer to cv T”, the result is static_cast<cv T*>(static_cast<cv void*>(v)).
[Note 5:
Converting a pointer of type “pointer to T1” that points to an object of type T1to the type “pointer to T2” (where T2 is an object type and the alignment requirements of T2are no stricter than those of T1) and back to its original type yields the original pointer value.
— _end note_]
Converting a function pointer to an object pointer type or vice versa is conditionally-supported.
The meaning of such a conversion isimplementation-defined, except that if an implementation supports conversions in both directions, converting a prvalue of one type to the other type and back, possibly with different cv-qualification, shall yield the original pointer value.
The null pointer value ([basic.compound]) is converted to the null pointer value of the destination type.
[Note 6:
A null pointer constant of type std::nullptr_t cannot be converted to a pointer type, and a null pointer constant of integral type is not necessarily converted to a null pointer value.
— _end note_]
A prvalue of type “pointer to member of X of type T1” can be explicitly converted to a prvalue of a different type “pointer to member ofY of type T2” if T1 and T2 are both function types or both object types.54
The null member pointer value ([conv.mem]) is converted to the null member pointer value of the destination type.
The result of this conversion is unspecified, except in the following cases:
- Converting a prvalue of type “pointer to member function” to a different pointer-to-member-function type and back to its original type yields the original pointer-to-member value.
- Converting a prvalue of type “pointer to data member of Xof type T1” to the type “pointer to data member of Yof type T2” (where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer-to-member value.
If v is a glvalue of type T1, designating an object or function x, it can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast.
The result is that of *reinterpret_cast<T2 *>(p)where p is a pointer to _x_of type “pointer to T1”.
7.6.1.11 Const cast [expr.const.cast]
The result of the expression const_cast<T>(v) is of typeT.
If T is an lvalue reference to object type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and thelvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions are performed on the expression v.
Conversions that can be performed explicitly usingconst_cast are listed below.
No other conversion shall be performed explicitly using const_cast.
[Note 1:
Subject to the restrictions in this subclause, an expression can be cast to its own type using a const_cast operator.
— _end note_]
For two similar object pointer or pointer to data member typesT1 and T2 ([conv.qual]), a prvalue of type T1 can be explicitly converted to the type T2 using a const_castif, considering the qualification-decompositions of both types, each is the same as for all i.
If v is a null pointer or null member pointer, the result is a null pointer or null member pointer, respectively.
Otherwise, the result points to or past the end of the same object, or points to the same member, respectively, as v.
For two object types T1 and T2, if a pointer to T1 can be explicitly converted to the type “pointer to T2” using aconst_cast, then the following conversions can also be made:
- an lvalue of type T1 can be explicitly converted to an lvalue of type T2 using the cast const_cast<T2&>;
- a glvalue of type T1 can be explicitly converted to an xvalue of type T2 using the cast const_cast<T2&&>; and
- if T1 is a class or array type, a prvalue of type T1 can be explicitly converted to an xvalue of type T2 using the castconst_cast<T2&&>.
The temporary materialization conversion is performed on v.
The result refers to the same object as the (possibly converted) operand.
[Example 1: typedef int *A[3]; typedef const int *const CA[3]; auto &&r2 = const_cast<A&&>(CA{}); — _end example_]
[Note 2:
Depending on the type of the object, a write operation through the pointer, lvalue or pointer to data member resulting from aconst_cast that casts away a const-qualifier56can produce undefined behavior ([dcl.type.cv]).
— _end note_]
A conversion from a type T1 to a type T2 casts away constnessif T1 and T2 are different, there is a qualification-decomposition ([conv.qual]) of T1yielding n such thatT2 has a qualification-decomposition of the form ⋯ ,and there is no qualification conversion that converts T1 to ⋯ .
Casting from an lvalue of type T1 to an lvalue of typeT2 using an lvalue reference cast or casting from an expression of type T1 to an xvalue of type T2 using an rvalue reference cast casts away constness if a cast from a prvalue of type “pointer to T1” to the type “pointer toT2” casts away constness.
[Note 3:
Some conversions which involve only changes in cv-qualification cannot be done using const_cast.
For instance, conversions between pointers to functions are not covered because such conversions lead to values whose use causes undefined behavior.
For the same reasons, conversions between pointers to member functions, and in particular, the conversion from a pointer to a const member function to a pointer to a non-const member function, are not covered.
— _end note_]
7.6.2 Unary expressions [expr.unary]
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 thancv 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 mof 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 ... int a;int* p1 = &a;int* p2 = p1 + 1; bool b = p2 > p1; — _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 thequalified-id is enclosed in parentheses, does not form an expression of type “pointer to member”.
Neither doesqualified-id, because there is no implicit conversion from aqualified-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 trueif 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 ris 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_]
The ambiguity is resolved by treating ~ as the operator rather than as the start of an unqualified-idnaming 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.
The expression ++x is otherwise equivalent to x+=1 and the expression --x is otherwise equivalent to x-=1 ([expr.assign]).
[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 not appear in the initializer of a block variable with static or thread storage duration.
An await-expression shall not be a potentially-evaluated subexpression of the predicate of a contract assertion ([basic.contract]).
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_transformin 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 applicableoperator 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 typestd::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, orstd::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 type of await-suspend_is std::coroutine_handle<Z>,await-suspend.resume() is evaluated.
- 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.
[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()); int a[] = { co_await h() }; — _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 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 sizeofapplied to any of the narrow character types is 1.
The result ofsizeof 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.57
— _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.58
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 isn times the size of an element.
The identifier in a sizeof... expression shall name a pack.
The sizeof... operator yields the number of elements in the pack ([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 typestd::size_t.
[Note 3:
A sizeof expression is an integral constant expression ([expr.const]).
— _end note_]
7.6.2.6 Alignof [expr.alignof]
An alignof expression yields the alignment requirement of its operand type.
The operand shall be a type-idrepresenting 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]).
— _end note_]
When alignof is applied to a reference type, the result is the alignment of the referenced type.
When alignofis 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]), andtrue otherwise.
7.6.2.8 New [expr.new]
The new-expression attempts to create an object of thetype-id or new-type-id ([dcl.name]) to which it is applied.
The type of that object is the allocated type.
[Note 1:
Because references are not objects, references cannot be created bynew-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_]
[Example 1: new auto(1); auto x = new auto('a'); template<class T> struct A { A(T, T); };auto y = new A{1, 2}; — _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;
The * is the pointer declarator and not the multiplication operator.
— _end example_]
[Note 4:
Parentheses in a new-type-id of a new-expressioncan have surprising effects.
[Example 3:
new int(*[10])(); is ill-formed because the binding is(new int) (*[10])();
Instead, the explicitly parenthesized version of the newoperator 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_]
Every constant-expression in anoptr-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 theexpression of a noptr-new-declarator), butnew float[5][n] is ill-formed (because n is not a constant expression).
Furthermore,new float[0] is well-formed (because 0 is the expressionof a noptr-new-declarator, where a value of zero results in the allocation of an array with no elements), but new float[n][0] is ill-formed (because 0 is the constant-expressionof a noptr-new-declarator, where only values greater than zero are allowed).
— _end example_]
If the expression in a noptr-new-declaratoris 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 tostd::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])59is less than zero;
- its value is such that the size of the allocated object would exceed theimplementation-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:
- if the expression is a potentially-evaluated core constant expression, the program is ill-formed;
- otherwise, an allocation function is not called; instead
- if the allocation function that would have been called has a non-throwing exception specification ([except.spec]), the value of the new-expressionis the null pointer value of the required result type;
- otherwise, the new-expression terminates by throwing an exception of a type that would match a handler ([except.handle]) of typestd::bad_array_new_length.
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 expressionis 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_]
[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 thenew-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-expressionis 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_]
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 isoperator new and the deallocation function's name isoperator delete.
If the allocated type is an array type, the allocation function's name isoperator new[]and the deallocation function's name isoperator delete[].
[Note 8:
The set of allocation and deallocation functions that can be called by a new-expressioncan include functions that do not perform allocation or deallocation; for example, see [new.delete.placement].
— _end note_]
If the new-expressiondoes 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.
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 ofe2, 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 ore2 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 thedelete-expression whose operand is the pointer value produced by e1.
[Example 5: void can_merge(int x) { 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 { 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, thenew-expression passes the amount of space requested to the allocation function as the first argument of typestd::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 ofchar, 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 fundamentalalignment 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-clause_s_in its expression-listare 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_tis 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 thenew-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:
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 Tinitializes that object as follows:
- If the new-initializer is omitted, the object is default-initialized ([dcl.init]).
[Note 12:
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 sequenced before the evaluations of expressions in the new-initializer.
Initialization of the allocated object is sequenced before thevalue computation of thenew-expression.
If the new-expressioncreates an array of objects of class type, the destructor is potentially invoked ([class.dtor]).
If any part of the object initialization described above60terminates 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 { static void* operator new(std::size_t, std::size_t);static void operator delete(void*, std::size_t);}; S* p = new (0) S; — _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.
7.6.2.9 Delete [expr.delete]
delete-expression:
:: delete cast-expression
:: delete [ ] cast-expression
The first alternative is asingle-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.61
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 typevoid.
In a single-object delete expression, the value of the operand ofdelete 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 deletemay 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]).62
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 thenew-expression.
— _end note_]
[Note 2:
A pointer to a const type can be the operand of adelete-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:
- If the allocation call for the new-expression for the object to be deleted was not omitted and the allocation was not extended ([expr.new]), thedelete-expression shall call a deallocation function.
The value returned from the allocation call of the new-expression shall be passed as the first argument to the deallocation function. - Otherwise, if the allocation was extended or was provided by extending the allocation of another new-expression, and thedelete-expression for every other pointer value produced by anew-expression that had storage provided by the extendednew-expression has been evaluated, thedelete-expression shall call a deallocation function.
The value returned from the allocation call of the extended new-expressionshall be passed as the first argument to the deallocation function. - Otherwise, the delete-expression will not call a deallocation function.
[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 oroperator delete[] for an array delete expression.
If the keyword delete in a delete-expressionis not preceded by the unary :: operator and the type of the operand is a pointer to a (possibly cv-qualified) class type Tor (possibly multidimensional) array thereof:
- For a single-object delete expression, if the operand is a pointer to cv T andT 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.
[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_tis 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-expressionis 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_tis 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_]
7.6.3 Explicit type conversion (cast notation) [expr.cast]
The result of the expression (T) cast-expression is of type T.
The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type and an xvalue if Tis an rvalue reference to object type; otherwise the result is a prvalue.
[Note 1:
If T is a non-class type that is cv-qualified, thecv-qualifiers are discarded when determining the type of the resulting prvalue; see [expr.prop].
— _end note_]
An explicit type conversion can be expressed using functional notation ([expr.type.conv]), a type conversion operator (dynamic_cast, static_cast, reinterpret_cast,const_cast), or the cast notation.
Any type conversion not mentioned below and not explicitly defined by the user ([class.conv]) is ill-formed.
The conversions performed by
- a const_cast ([expr.const.cast]),
- a static_cast ([expr.static.cast]),
- a static_cast followed by a const_cast,
- a reinterpret_cast ([expr.reinterpret.cast]), or
- a reinterpret_cast followed by a const_cast,
can be performed using the cast notation of explicit type conversion.
The same semantic restrictions and behaviors apply, with the exception that in performing a static_cast in the following situations the conversion is valid even if the base class is inaccessible:
- a pointer to an object of derived class type or an lvalue or rvalue of derived class type may be explicitly converted to a pointer or reference to an unambiguous base class type, respectively;
- a pointer to member of derived class type may be explicitly converted to a pointer to member of an unambiguous non-virtual base class type;
- a pointer to an object of an unambiguous non-virtual base class type, a glvalue of an unambiguous non-virtual base class type, or a pointer to member of an unambiguous non-virtual base class type may be explicitly converted to a pointer, a reference, or a pointer to member of a derived class type, respectively.
If a conversion can be interpreted in more than one of the ways listed above, the interpretation that appears first in the list is used, even if a cast resulting from that interpretation is ill-formed.
If astatic_cast followed by a const_cast is used and the conversion can be interpreted in more than one way as such, the conversion is ill-formed.
[Example 1: struct A { };struct I1 : A { };struct I2 : A { };struct D : I1, I2 { }; A* foo( D* p ) { return (A*)( p ); } int*** ptr = 0;auto t = (int const*const*const*)ptr; struct S { operator const int*();operator volatile int*();};int *p = (int*)S(); — _end example_]
The operand of a cast using the cast notation can be a prvalue of type “pointer to incomplete class type”.
The destination type of a cast using the cast notation can be “pointer to incomplete class type”.
If both the operand and destination types are class types and one or both are incomplete, it is unspecified whether the static_cast or thereinterpret_cast interpretation is used, even if there is an inheritance relationship between the two classes.
[Note 2:
For example, if the classes were defined later in the translation unit, a multi-pass compiler could validly interpret a cast between pointers to the classes as if the class types were complete at the point of the cast.
— _end note_]
7.6.4 Pointer-to-member operators [expr.mptr.oper]
The pointer-to-member operators ->* and .* group left-to-right.
The binary operator .* binds its second operand, which shall be a prvalue of type “pointer to member of T” to its first operand, which shall be a glvalue of class T or of a class of which T is an unambiguous and accessible base class.
The result is an object or a function of the type specified by the second operand.
The binary operator ->* binds its second operand, which shall be a prvalue of type “pointer to member of T” to its first operand, which shall be of type “pointer to U” where U is either T or a class of which Tis an unambiguous and accessible base class.
The expression E1->*E2 is converted into the equivalent form(*(E1)).*E2.
Abbreviating pm-expression.*cast-expression as E1.*E2, E1is called the object expression.
If the result of E1 is an object whose type is not similar to the type of E1, or whose most derived object does not contain the member to whichE2 refers, the behavior is undefined.
The expression E1 is sequenced before the expression E2.
The restrictions on cv-qualification, and the manner in which the cv-qualifiers of the operands are combined to produce the cv-qualifiers of the result, are the same as the rules forE1.E2 given in [expr.ref].
[Note 1:
It is not possible to use a pointer to member that refers to amutable member to modify a const class object.
For example,struct S { S() : i(0) { } mutable int i;};void f() { const S cs;int S::* pm = &S::i; cs.*pm = 88; }
— _end note_]
If the result of .* or ->* is a function, then that result can be used only as the operand for the function call operator().
[Example 1:
(ptr_to_obj->*ptr_to_mfct)(10);calls the member function denoted by ptr_to_mfct for the object pointed to by ptr_to_obj.
— _end example_]
In a .* expression whose object expression is an rvalue, the program is ill-formed if the second operand is a pointer to member function whose ref-qualifier is &, unless its cv-qualifier-seq is const.
In a .*expression whose object expression is an lvalue, the program is ill-formed if the second operand is a pointer to member function whose ref-qualifier is &&.
The result of a .* expression whose second operand is a pointer to a data member is an lvalue if the first operand is an lvalue and an xvalue otherwise.
The result of a .* expression whose second operand is a pointer to a member function is a prvalue.
7.6.5 Multiplicative operators [expr.mul]
The multiplicative operators *, /, and % group left-to-right.
The operands of * and / shall have arithmetic or unscoped enumeration type; the operands of % shall have integral or unscoped enumeration type.
The binary * operator indicates multiplication.
The binary / operator yields the quotient, and the binary% operator yields the remainder from the division of the first expression by the second.
If the second operand of / or % is zero, the behavior is undefined.
For integral operands, the / operator yields the algebraic quotient with any fractional part discarded;63if the quotient a/b is representable in the type of the result,(a/b)*b + a%b is equal to a; otherwise, the behavior of both a/b and a%b is undefined.
7.6.6 Additive operators [expr.add]
The additive operators + and - group left-to-right.
Each operand shall be a prvalue.
If both operands have arithmetic or unscoped enumeration type, the usual arithmetic conversions ([expr.arith.conv]) are performed.
Otherwise, if one operand has arithmetic or unscoped enumeration type, integral promotion is applied ([conv.prom]) to that operand.
A converted or promoted operand is used in place of the corresponding original operand for the remainder of this section.
For addition, either both operands shall have arithmetic type, or one operand shall be a pointer to a completely-defined object type and the other shall have integral type.
For subtraction, one of the following shall hold:
- both operands have arithmetic type; or
- both operands are pointers to cv-qualified or cv-unqualified versions of the same completely-defined object type; or
- the left operand is a pointer to a completely-defined object type and the right operand has integral type.
The result of the binary + operator is the sum of the operands.
The result of the binary - operator is the difference resulting from the subtraction of the second operand from the first.
When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.
- If P evaluates to a null pointer value andJ evaluates to 0, the result is a null pointer value.
- Otherwise, if P points to a (possibly-hypothetical) array element iof an array object x with n elements ([dcl.array]),64the expressions P + J and J + P(where J has the value j) point to the (possibly-hypothetical) array element of x if and the expression P - Jpoints to the (possibly-hypothetical) array element of x if .
- Otherwise, the behavior is undefined.
[Note 1:
Adding a value other than 0 or 1to a pointer to a base class subobject, a member subobject, or a complete object results in undefined behavior.
— _end note_]
When two pointer expressions P and Q are subtracted, the type of the result is an implementation-defined signed integral type; this type shall be the same type that is named bystd::ptrdiff_t in the header ([support.types.layout]).
- If P and Q both evaluate to null pointer values, the result is 0.
- Otherwise, if P and Q point to, respectively, array elements i and jof the same array object x, the expression P - Q has the value .
[Note 2:
If the value is not in the range of representable values of type std::ptrdiff_t, the behavior is undefined ([expr.pre]).
— _end note_] - Otherwise, the behavior is undefined.
For addition or subtraction, if the expressions P or Q have type “pointer to cv T”, where T and the array element type are not similar, the behavior is undefined.
[Example 1: int arr[5] = {1, 2, 3, 4, 5};unsigned int *p = reinterpret_cast<unsigned int*>(arr + 1);unsigned int k = *p; unsigned int *q = p + 1; — _end example_]
7.6.7 Shift operators [expr.shift]
The shift operators << and >> group left-to-right.
The operands shall be prvalues of integral or unscoped enumeration type and integral promotions are performed.
The type of the result is that of the promoted left operand.
The behavior is undefined if the right operand is negative, or greater than or equal to the width of the promoted left operand.
The value of E1 << E2 is the unique value congruent to modulo , where N is the width of the type of the result.
[Note 1:
E1 is left-shifted E2 bit positions; vacated bits are zero-filled.
— _end note_]
The value of E1 >> E2 is , rounded towards negative infinity.
[Note 2:
E1 is right-shifted E2 bit positions.
Right-shift on signed integral types is an arithmetic right shift, which performs sign-extension.
— _end note_]
The expression E1 is sequenced before the expression E2.
7.6.8 Three-way comparison operator [expr.spaceship]
The three-way comparison operator groups left-to-right.
The expression p <=> q is a prvalue indicating whetherp is less than, equal to, greater than, or incomparable withq.
If one of the operands is of type booland the other is not, the program is ill-formed.
If both operands have arithmetic types, or one operand has integral type and the other operand has unscoped enumeration type, the usual arithmetic conversions are applied to the operands.
Then:
- If a narrowing conversion is required, other than from an integral type to a floating-point type, the program is ill-formed.
- Otherwise, if the operands have integral type, the result is of type std::strong_ordering.
The result isstd::strong_ordering::equalif both operands are arithmetically equal,std::strong_ordering::lessif the first operand is arithmetically less than the second operand, andstd::strong_ordering::greaterotherwise. - Otherwise, the operands have floating-point type, and the result is of type std::partial_ordering.
The expression a <=> b yieldsstd::partial_ordering::lessif a is less than b,std::partial_ordering::greaterif a is greater than b,std::partial_ordering::equivalentif a is equivalent to b, andstd::partial_ordering::unordered otherwise.
If both operands have the same enumeration type E, the operator yields the result of converting the operands to the underlying type of Eand applying <=> to the converted operands.
If at least one of the operands is of object pointer type and the other operand is of object pointer or array type, array-to-pointer conversions ([conv.array]), pointer conversions ([conv.ptr]), andqualification conversionsare performed on both operands to bring them to their composite pointer type ([expr.type]).
After the conversions, the operands shall have the same type.
In this case,p <=> q is of type std::strong_ordering and the result is defined by the following rules:
- If two pointer operands p and q compare equal ([expr.eq]),p <=> q yields std::strong_ordering::equal;
- otherwise, if p and q compare unequal,p <=> q yieldsstd::strong_ordering::lessif q compares greater than pandstd::strong_ordering::greaterif p compares greater than q ([expr.rel]);
- otherwise, the result is unspecified.
Otherwise, the program is ill-formed.
The three comparison category types ([cmp.categories]) (the typesstd::strong_ordering,std::weak_ordering, andstd::partial_ordering) are not predefined; if a standard library declaration ([compare.syn], [std.modules]) of such a class type does not precede ([basic.lookup.general]) a use of that type — even an implicit use in which the type is not named (e.g., via the auto specifier ([dcl.spec.auto]) in a defaulted three-way comparison ([class.spaceship]) or use of the built-in operator) — the program is ill-formed.
7.6.9 Relational operators [expr.rel]
The relational operators group left-to-right.
[Example 1:
a<b<c means (a<b)<c and not (a<b)&&(b<c).
— _end example_]
The lvalue-to-rvalue ([conv.lval]) and function-to-pointer ([conv.func]) standard conversions are performed on the operands.
If one of the operands is a pointer, the array-to-pointer conversion ([conv.array]) is performed on the other operand.
The converted operands shall have arithmetic, enumeration, or pointer type.
The operators < (less than), > (greater than), <=(less than or equal to), and >= (greater than or equal to) all yield false or true.
The type of the result isbool.
The usual arithmetic conversions ([expr.arith.conv]) are performed on operands of arithmetic or enumeration type.
After conversions, the operands shall have the same type.
The result of comparing unequal pointers to objects65is defined in terms of a partial order consistent with the following rules:
- If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to the element with the higher subscript is required to compare greater.
- If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the pointer to the later declared member is required to compare greater provided neither member is a subobject of zero size and their class is not a union.
- Otherwise, neither pointer is required to compare greater than the other.
If two operands p and q compare equal ([expr.eq]),p<=q and p>=q both yield true and p<q andp>q both yield false.
Otherwise, if a pointer to object pcompares greater than a pointer q, p>=q, p>q,q<=p, and q<p all yield true and p<=q,p<q, q>=p, and q>p all yield false.
Otherwise, the result of each of the operators is unspecified.
[Note 1:
A relational operator applied to unequal function pointers yields an unspecified result.
A pointer value of type “pointer to cv void” can point to an object ([basic.compound]).
— _end note_]
If both operands (after conversions) are of arithmetic or enumeration type, each of the operators shall yield true if the specified relationship is true and false if it is false.
7.6.10 Equality operators [expr.eq]
The == (equal to) and the != (not equal to) operators group left-to-right.
The lvalue-to-rvalue ([conv.lval]) and function-to-pointer ([conv.func]) standard conversions are performed on the operands.
If one of the operands is a pointer or a null pointer constant ([conv.ptr]), the array-to-pointer conversion ([conv.array]) is performed on the other operand.
The converted operands shall have scalar type.
The operators== and != both yield true or false, i.e., a result of type bool.
In each case below, the operands shall have the same type after the specified conversions have been applied.
Comparing pointers is defined as follows:
- If one pointer represents the address of a complete object, and another pointer represents the address one past the last element of a different complete object,66the result of the comparison is unspecified.
- Otherwise, the pointers compare unequal.
If at least one of the operands is a pointer to member, pointer-to-member conversions ([conv.mem]), function pointer conversions ([conv.fctptr]), and qualification conversions ([conv.qual]) are performed on both operands to bring them to their composite pointer type ([expr.type]).
Comparing pointers to members is defined as follows:
- If two pointers to members are both the null member pointer value, they compare equal.
- If only one of two pointers to members is the null member pointer value, they compare unequal.
- If either is a pointer to a virtual member function, the result is unspecified.
- If one refers to a member of class C1 and the other refers to a member of a different class C2, where neither is a base class of the other, the result is unspecified.
[Example 1: struct A {};struct B : A { int x; };struct C : A { int x; };int A::*bx = (int(A::*))&B::x;int A::*cx = (int(A::*))&C::x;bool b1 = (bx == cx); — _end example_] - If both refer to (possibly different) members of the same union, they compare equal.
- Otherwise, two pointers to members compare equal if they would refer to the same member of the same most derived object or the same subobject if indirection with a hypothetical object of the associated class type were performed, otherwise they compare unequal.
[Example 2: struct B { int f();};struct L : B { };struct R : B { };struct D : L, R { };int (B::*pb)() = &B::f;int (L::*pl)() = pb;int (R::*pr)() = pb;int (D::*pdl)() = pl;int (D::*pdr)() = pr;bool x = (pdl == pdr); bool y = (pb == pl); — _end example_]
Two operands of type std::nullptr_t or one operand of typestd::nullptr_t and the other a null pointer constant compare equal.
If two operands compare equal, the result is true for the == operator and false for the != operator.
If two operands compare unequal, the result is false for the == operator andtrue for the != operator.
Otherwise, the result of each of the operators is unspecified.
If both operands are of arithmetic or enumeration type, the usual arithmetic conversions ([expr.arith.conv]) are performed on both operands; each of the operators shall yieldtrue if the specified relationship is true and false if it is false.
7.6.11 Bitwise AND operator [expr.bit.and]
The & operator groups left-to-right.
The operands shall be of integral or unscoped enumeration type.
Given the coefficients and of the base-2 representation ([basic.fundamental]) of the converted operands x and y, the coefficient of the base-2 representation of the result ris 1 if both and are 1, and 0 otherwise.
[Note 1:
The result is the bitwise and function of the operands.
— _end note_]
7.6.12 Bitwise exclusive OR operator [expr.xor]
The ^ operator groups left-to-right.
The operands shall be of integral or unscoped enumeration type.
Given the coefficients and of the base-2 representation ([basic.fundamental]) of the converted operands x and y, the coefficient of the base-2 representation of the result ris 1 if either (but not both) of and is 1, and 0 otherwise.
[Note 1:
The result is the bitwise exclusive or function of the operands.
— _end note_]
7.6.13 Bitwise inclusive OR operator [expr.or]
The | operator groups left-to-right.
The operands shall be of integral or unscoped enumeration type.
Given the coefficients and of the base-2 representation ([basic.fundamental]) of the converted operands x and y, the coefficient of the base-2 representation of the result ris 1 if at least one of and is 1, and 0 otherwise.
[Note 1:
The result is the bitwise inclusive or function of the operands.
— _end note_]
7.6.14 Logical AND operator [expr.log.and]
The && operator groups left-to-right.
The operands are both contextually converted to bool ([conv]).
The result is true if both operands are true andfalse otherwise.
Unlike &, && guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is false.
The result is a bool.
If the second expression is evaluated, the first expression is sequenced before the second expression ([intro.execution]).
7.6.15 Logical OR operator [expr.log.or]
The || operator groups left-to-right.
The operands are both contextually converted to bool ([conv]).
The result istrue if either of its operands is true, andfalse otherwise.
Unlike |, || guarantees left-to-right evaluation; moreover, the second operand is not evaluated if the first operand evaluates to true.
The result is a bool.
If the second expression is evaluated, the first expression is sequenced before the second expression ([intro.execution]).
7.6.16 Conditional operator [expr.cond]
Conditional expressions group right-to-left.
The first expression is contextually converted to bool ([conv]).
It is evaluated and if it is true, the result of the conditional expression is the value of the second expression, otherwise that of the third expression.
Only one of the second and third expressions is evaluated.
The first expression is sequenced before the second or third expression ([intro.execution]).
If either the second or the third operand has type void, one of the following shall hold:
- The second or the third operand (but not both) is a (possibly parenthesized) throw-expression ([expr.throw]); the result is of the type and value category of the other.
The conditional-expressionis a bit-field if that operand is a bit-field. - Both the second and the third operands have type void; the result is of type void and is a prvalue.
[Note 1:
This includes the case where both operands are throw-expressions.
— _end note_]
Otherwise, if the second and third operand are glvalue bit-fields of the same value category and of types cv1 T and cv2 T, respectively, the operands are considered to be of type cv Tfor the remainder of this subclause, where cv is the union of cv1 and cv2.
Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to form an implicit conversion sequence from each of those operands to the type of the other.
[Note 2:
Properties such as access, whether an operand is a bit-field, or whether a conversion function is deleted are ignored for that determination.
— _end note_]
Attempts are made to form an implicit conversion sequence from an operand expression E1 of type T1to a target type related to the type T2of the operand expression E2 as follows:
- If E2 is an lvalue, the target type is “lvalue reference to T2”, but an implicit conversion sequence can only be formed if the reference would bind directly ([dcl.init.ref]) to a glvalue.
- If E2 is an xvalue, the target type is “rvalue reference to T2”, but an implicit conversion sequence can only be formed if the reference would bind directly.
- If E2 is a prvalue or if neither of the conversion sequences above can be formed and at least one of the operands has (possibly cv-qualified) class type:
- if T1 and T2 are the same class type (ignoring cv-qualification):
* if T2 is at least as cv-qualified as T1, the target type is T2,
* otherwise, no conversion sequence is formed for this operand; - otherwise, if T2 is a base class of T1, the target type is cv1 T2, where _cv1_denotes the cv-qualifiers of T1;
- otherwise, the target type is the type that E2 would have after applying thelvalue-to-rvalue,array-to-pointer, andfunction-to-pointerstandard conversions.
- if T1 and T2 are the same class type (ignoring cv-qualification):
Using this process, it is determined whether an implicit conversion sequence can be formed from the second operand to the target type determined for the third operand, and vice versa, with the following outcome:
- If both sequences can be formed, or one can be formed but it is the ambiguous conversion sequence, the program is ill-formed.
- If no conversion sequence can be formed, the operands are left unchanged and further checking is performed as described below.
- Otherwise, if exactly one conversion sequence can be formed, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remainder of this subclause.
[Note 3:
The conversion might be ill-formed even if an implicit conversion sequence could be formed.
— _end note_]
If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category and it is a bit-field if the second or the third operand is a bit-field, or if both are bit-fields.
Otherwise, the result is a prvalue.
If the second and third operands do not have the same type, and either has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands ([over.match.oper], [over.built]).
If the overload resolution fails, the program is ill-formed.
Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this subclause.
After those conversions, one of the following shall hold:
- The second and third operands have the same type; the result is of that type and the result is copy-initialized using the selected operand.
- The second and third operands have arithmetic or enumeration type; the usual arithmetic conversions are performed to bring them to a common type, and the result is of that type.
- The result is of the composite pointer type.
- One or both of the second and third operands have pointer-to-member type; lvalue-to-rvalue ([conv.lval]), pointer to member ([conv.mem]), function pointer ([conv.fctptr]), and qualification conversions ([conv.qual]) are performed to bring them to their composite pointer type ([expr.type]).
The result is of the composite pointer type. - Both the second and third operands have type std::nullptr_t or one has that type and the other is a null pointer constant.
The result is of typestd::nullptr_t.
7.6.17 Yielding a value [expr.yield]
A yield-expression shall appear only within a suspension context of a function ([expr.await]).
Let e be the operand of the yield-expression and_p_ be an lvalue naming the promise object of the enclosing coroutine ([dcl.fct.def.coroutine]), then the yield-expressionis equivalent to the expressionco_await p.yield_value(e).
[Example 1: template <typename T> struct my_generator { struct promise_type { T current_value;auto yield_value(T v) { current_value = std::move(v);return std::suspend_always{};} };struct iterator { }; iterator begin(); iterator end();}; my_generator<pair<int,int>> g1() { for (int i = 0; i < 10; ++i) co_yield {i,i};}my_generator<pair<int,int>> g2() { for (int i = 0; i < 10; ++i) co_yield make_pair(i,i);} auto f(int x = co_yield 5); int a[] = { co_yield 1 }; int main() { auto r1 = g1();auto r2 = g2(); assert(std::equal(r1.begin(), r1.end(), r2.begin(), r2.end()));} — _end example_]
7.6.18 Throwing an exception [expr.throw]
A throw-expression is of type void.
The array-to-pointer ([conv.array]) and function-to-pointer ([conv.func]) standard conversions are performed on the operand.
The type of the exception object is determined by removing any top-level cv-qualifiers from the type of the (possibly converted) operand.
The exception object is copy-initialized ([dcl.init.general]) from the (possibly converted) operand.
Athrow-expressionwith no operand rethrows the currently handled exception ([except.handle]).
If no exception is presently being handled, the function std::terminate is invoked ([except.terminate]).
Otherwise, the exception is reactivated with the existing exception object; no new exception object is created.
The exception is no longer considered to be caught.
[Example 1:
An exception handler that cannot completely handle the exception itself can be written like this:try { } catch (...) { throw; }
— _end example_]
7.6.19 Assignment and compound assignment operators [expr.assign]
The assignment operator (=) and the compound assignment operators all group right-to-left.
All require a modifiable lvalue as their left operand; their result is an lvalue of the type of the left operand, referring to the left operand.
The result in all cases is a bit-field if the left operand is a bit-field.
In all cases, the assignment is sequenced after thevalue computation of the right and left operands, and before the value computation of the assignment expression.
The right operand is sequenced before the left operand.
With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.
[Note 1:
Therefore, a function call cannot intervene between the lvalue-to-rvalue conversion and the side effect associated with any single compound assignment operator.
— _end note_]
assignment-operator: one of
= *= /= %= += -= >>= <<= &= ^= |=
In simple assignment (=), let V be the result of the right operand; the object referred to by the left operand is modified ([defns.access]) by replacing its value with V or, if the object is of integer type, with the value congruent ([basic.fundamental]) to V.
If the right operand is an expression, it is implicitlyconverted to the cv-unqualified type of the left operand.
When the left operand of an assignment operator is a bit-field that cannot represent the value of the expression, the resulting value of the bit-field isimplementation-defined.
An assignment whose left operand is of a volatile-qualified type is deprecated ([depr.volatile.type]) unless the (possibly parenthesized) assignment is a discarded-value expression or an unevaluated operand.
The behavior of an expression of the form E1 _op_= E2is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.
[Note 2:
The object designated by E1 is accessed twice.
— _end note_]
For += and -=,E1 shall either have arithmetic type or be a pointer to a possibly cv-qualified completely-defined object type.
In all other cases, E1 shall have arithmetic type.
If the value being stored in an object is read via another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined.
[Note 3:
This restriction applies to the relationship between the left and right sides of the assignment operation; it is not a statement about how the target of the assignment can be aliased in general.
— _end note_]
- an assignment to a scalar of type T, in which case Bshall have at most a single element.
The meaning of x = B is x = t, where t is an invented temporary variable declared and initialized as T t = B. - an assignment to an object of class type, in which case Bis passed as the argument to the assignment operator function selected by overload resolution ([over.assign], [over.match]).
[Example 1: complex<double> z; z = { 1,2 }; z += { 1, 2 }; int a, b; a = b = { 1 }; a = { 1 } = b; — _end example_]
7.6.20 Comma operator [expr.comma]
The comma operator groups left-to-right.
A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded-value expression.
The left expression is sequenced before the right expression ([intro.execution]).
The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand, and is a bit-field if its right operand is a bit-field.
[Note 1:
In contexts where the comma token is given special meaning (e.g., function calls ([expr.call]), subscript expressions ([expr.sub]), lists of initializers ([dcl.init]), or template-argument-lists ([temp.names])), the comma operator as described in this subclause can appear only in parentheses.
[Example 1:
f(a, (t=3, t+2), c);has three arguments, the second of which has the value5.
— _end example_]
— _end note_]