[expr.post] (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.
— _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.
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.
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.
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.
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_]