The > token following the
type-id in a dynamic_cast,
static_cast, reinterpret_cast, or
const_cast can be the product of replacing a
>> token by two consecutive >
tokens ([temp.names]).
A subscript expression is a postfix expression
followed by square brackets containing
a possibly empty, comma-separated list of initializer-clauses
that constitute the arguments to the subscript operator.
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.
One of the expressions shall be a glvalue of type “array of
T” or a prvalue of type “pointer
to T” and the other shall be a prvalue of unscoped enumeration or integral type.
The type “T” shall be a completely-defined object type.44
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.
A function call is a postfix expression followed by parentheses
containing a possibly empty, comma-separated list of
initializer-clauses which
constitute the arguments to the function.
If the postfix expression is a 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 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-expression
is 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 Tcall is
call-compatible with a function type Tfunc
if Tcall is the same type as Tfunc or
if the type “pointer to Tfunc” can be
converted to type “pointer to Tcall”
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.
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.
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>(); / error: no argument for second function parameter — 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.
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.
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.
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.
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.
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"); / OK} — end example]
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);
};
intoperator<(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.
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.
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.
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.
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]).
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.
An argument that has type cvstd::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 with
implementation-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 the
floating-point promotion, the value of the argument is converted to the
promoted type before the call.
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].
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 deduction
for 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]).
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 cvvoid,
the initializer shall be () or {}
(after pack expansion, if any), and
the expression is a prvalue of type void
that 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 if
T is an lvalue reference type or
an rvalue reference to function type and
an xvalue otherwise.
A postfix expression followed by a dot . or an arrow ->,
optionally followed by the keyword
template, and then followed by an
id-expression or a splice-expression,
is a postfix expression.
For a dot that is followed by an expression
that designates a static member or an enumerator,
the first expression is a discarded-value expression ([expr.context]);
if the expression after the dot designates a
non-static data member ([class.mem.general]) or
a direct base class relationship ([class.derived.general]),
the first expression shall be a glvalue.
A postfix expression that is followed by an arrow
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 form using a dot.45
The postfix expression before the dot is evaluated;46
the result of that evaluation,
together with
the id-expression or splice-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) and
E1.E2 is a prvalue of type “function of () returning void”.
If E2 designates an entity
that is declared to have type “reference to T”, then
E1.E2 is an lvalue of type T.
In that case, if E2 designates 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, if E2 designates a non-static data member and the type of
E1 is “cq1 vq1X”, and the type of E2
is “cq2 vq2T”, the expression designates the corresponding
member subobject of the object designated by E1.
If E1
is 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 is
const, then cq12 is const.
If the entity designated by E2
is declared to be a mutable member,
then the type of E1.E2 is “vq12T”.
If the entity designated by E2
is not declared to be a mutable member,
then the type of E1.E2 is “cq12vq12T”.
Otherwise, if E2 denotes 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 E2
and E1.E2 refers to the function referred to by E2.
Otherwise, if E2 designates a member enumerator and the type of E2
is T, the expression E1.E2 is a prvalue of type T
whose value is the value of the enumerator.
Otherwise, if E2 designates a direct base class relationship (D,B) and
D is either the cv-unqualified class type of E1 or a base class thereof,
let cv be the cv-qualification of the type of E1.
E1 is implicitly converted to the type “reference to cvD”
(where the reference is an lvalue reference if E1 is an lvalue
and an rvalue reference otherwise) and
the expression designates the direct base class subobject of type B
of the object designated by the converted E1.
If E1 is an lvalue,
then E1.E2 is an lvalue;
otherwise, E1.E2 is an xvalue.
[Example 1: struct B {int b;
};
struct C : B {int get()const{return b; }};
struct D : B, C {};
constexprint f(){
D d ={1, {};
/ b unambiguously refers to the direct base class of type B,/ not the indirect base class of type B
B& b = d.[: std::meta::bases_of(^D, std::meta::access_context::current()[0]:];
b.b +=10;
return10* b.b + d.get();
}static_assert(f()==110);
— end example]
If E2 designates a non-static member
(possibly after overload resolution),
the program is ill-formed if the class of which E2 designates
a direct member is an ambiguous base ([class.member.lookup]) of
the designating class ([class.access.base]) of E2.
If E2 designates a non-static member
(possibly after overload resolution)
or direct base class relationship 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 2: struct A {int i; };
struct B {int j; };
struct D : A, B {};
void f(){
D d;
static_cast<B&>(d).j; / OK, object expression designates the B subobject of dreinterpret_cast<B&>(d).j; / undefined behavior} — end example]
If the class member
access expression is evaluated, the subexpression evaluation happens even if the
result is unnecessary to determine
the value of the entire postfix expression, for example if the
id-expression denotes a static member.
Therefore, a function call cannot intervene between the
lvalue-to-rvalue conversion and the side effect associated with any
single postfix ++ operator.
If T is “pointer to cv1B” and v has
type “pointer to cv2D” such that B is a base
class of D, the result is a pointer to the unique B
subobject of the D object pointed to by v, or
a null pointer value if v is a null pointer value.
Similarly, if
T is “reference to cv1B” and v has
type cv2D such that B is a base class of
D, the result is the unique B subobject of the D
object referred to by v.47
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); / equivalent to B* bp = dp;} — end example]
If v has type “pointer to cvU” and
v 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 and
v 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, in the most derived object pointed (referred) to by v,
v points (refers) to a public base class subobject of a
C 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 the
C subobject of the most derived object.
[Example 2: class A {virtualvoid f(); };
class B {virtualvoid g(); };
class D :publicvirtual A, private B {};
void g(){
D d;
B* bp =(B*)&d; / cast needed to break protection
A* ap =&d; / public derivation, no cast needed
D& dr =dynamic_cast<D&>(*bp); / fails
ap =dynamic_cast<A*>(bp); / fails
bp =dynamic_cast<B*>(ap); / fails
ap =dynamic_cast<A*>(&d); / succeeds
bp =dynamic_cast<B*>(&d); / ill-formed (not a runtime check)}class E :public D, public B {};
class F :public E, public D {};
void h(){
F f;
A* ap =&f; / succeeds: finds unique A
D* dp =dynamic_cast<D*>(ap); / fails: yields null; f has two D subobjects
E* ep =(E*)ap; / error: cast from virtual base
E* ep1 =dynamic_cast<E*>(ap); / succeeds} — end example]
The result of a typeid expression is an lvalue of static type
conststd::type_info ([type.info]) and dynamic type conststd::type_info or constname where name is an
implementation-defined class publicly derived from
std::type_info which preserves the behavior described
in [type.info].48
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 the
std::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 a
std::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_info
object 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 the
type-id.
If the type of the type-id is a reference
to a possibly cv-qualified type, the result of the
typeid 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); / yields truetypeid(D)==typeid(const D); / yields truetypeid(D)==typeid(d2); / yields truetypeid(D)==typeid(const D&); / yields true — end example]
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.
An lvalue of type “cv1B”, where B is a class
type, can be cast to type “reference to cv2D”, where
D 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 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.
An xvalue of type
“cv1B” can be cast to type “rvalue reference to
cv2D” with the same constraints as for an lvalue of
type “cv1B”.
If the object
of type “cv1B” is actually a base class subobject of an object
of type D, the result refers to the enclosing object of type
D.
An lvalue
of type T1 can be cast to type “rvalue
reference to T2” if T2 is reference-compatible with
T1 ([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 conversion
is 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.
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.
Otherwise, an expression E can be explicitly converted to a type T
if 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 E
would 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 initialization
T 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.
Otherwise, the lvalue-to-rvalue ([conv.lval]),
array-to-pointer ([conv.array]), and
function-to-pointer ([conv.func]) conversions are applied to the
operand, and the conversions that can be performed using static_cast are listed below.
No other conversion can be performed using static_cast.
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.
A prvalue of type “pointer to cv1B”, where B
is a class type, can be converted to a prvalue of type “pointer to
cv2D”,
where D is a complete 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 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 cv1B” 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.
A prvalue of type “pointer to member of D of type cv1T” can be converted to a prvalue of type “pointer to member of
B of type cv2T”, where
D is a complete class type and
B is a base class of D,
if cv2 is the same cv-qualification
as, or greater cv-qualification than, cv1.
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 B
contains 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.
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].
A prvalue of type “pointer to cv1void” can be
converted to a prvalue of type “pointer to cv2T”,
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 address
A of a byte in memory and
A 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 T
that is pointer-interconvertible with a,
the result is a pointer to b.
Otherwise, the pointer value is unchanged by the conversion.
[Example 2: T* p1 =new T;
const T* p2 =static_cast<const T*>(static_cast<void*>(p1));
bool b = p1 == p2; / b will have the value true. — end example]
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 the
lvalue-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.
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.
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 otherwise
implementation-defined.
The effect of calling a function through a pointer to a function
type ([dcl.fct]) that is not call-compatible with the type used in the
definition of the function is undefined ([expr.call]).
An object pointer
can be explicitly converted to an object pointer of a different type.49
When a prvalue v of object pointer type is converted to
the object pointer type “pointer to cvT”, the result is static_cast<cv T*>(static_cast<cvvoid*>(v)).
Converting a pointer of type “pointer to T1”
that points to an object of type T1
to the type “pointer to T2”
(where T2 is an object type
and the alignment requirements of T2
are no stricter than those of T1)
and back to its original type yields the original pointer value.
Converting a function pointer to an object pointer
type or vice versa is
conditionally-supported.
The meaning of such a conversion is
implementation-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.
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.
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 of
Y of type T2” if T1 and T2 are both
function types or both object types.50
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 X
of type T1” to the type “pointer to data member of Y
of 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”.
No temporary is materialized ([conv.rval]) or created,
no copy is made, and
no constructors ([class.ctor]) or conversion
functions ([class.conv]) are called.51
The result of the expression const_cast<T>(v) is of type
T.
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 the
lvalue-to-rvalue, array-to-pointer,
and function-to-pointer standard conversions are
performed on the expression v.
For two similar object pointer or pointer to data member types
T1 and T2 ([conv.qual]),
a prvalue of type T1 can be explicitly
converted to the type T2 using a const_cast
if, considering the qualification-decompositions of both types,
each P1i is the same as P2i 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 a
const_cast, then the following conversions can also be made:
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 cast
const_cast<T2&&>.
The temporary materialization conversion is performed on v.
The result refers to the same object as the (possibly converted) operand.
[Example 1: typedefint*A[3]; / array of 3 pointer to inttypedefconstint*const CA[3]; / array of 3 const pointer to const intauto&&r2 =const_cast<A&&>(CA{}); / OK, temporary materialization conversion is performed — end example]
Depending on the type of the object, a write operation through the
pointer, lvalue or pointer to data member resulting from a
const_cast that casts away a const-qualifier52
can produce undefined behavior ([dcl.type.cv]).
A conversion from a type T1 to a type T2casts away constness
if T1 and T2 are different,
there is a qualification-decomposition ([conv.qual]) of T1
yielding n such that
T2 has a qualification-decomposition of the form
cv20P20cv21P21⋯cv2n−1P2n−1cv2nU2,
and there is no qualification conversion that converts T1 to