8 Expressions [expr]

8.2 Postfix expressions [expr.post]

8.2.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.

An lvalue of type “cv1 B”, where B is a class type, can be cast to type “reference to cv2 D”, where D is a 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 “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 type D. Otherwise, the behavior is undefined. [Example:

struct B { };
struct D : public B { };
D d;
B &br = d;

static_cast<D&>(br);            // produces lvalue to the original d object

end example]

An lvalue of type “cv1 T1” can be cast to type “rvalue reference to cv2 T2” if “cv2 T2” is reference-compatible with “cv1 T1”. 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 expression of the static_­cast for the remainder of this section. If T2 is an inaccessible or ambiguous base class of T1, a program that necessitates such a cast is ill-formed.

An expression e can be explicitly converted to a type T if there is an implicit conversion sequence from e to T, or if overload resolution for a direct-initialization of an object or reference of type T from e would find at least one viable function ([over.match.viable]). 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. [Note: The conversion is ill-formed when attempting to convert an expression of class type to an inaccessible or ambiguous base class. end note]

Otherwise, the static_­cast shall perform one of the conversions listed below. No other conversion shall be performed explicitly using a static_­cast.

Any expression can be explicitly converted to type cv void, in which case it becomes a discarded-value expression. [Note: However, if the value is in a temporary object, 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]

The inverse of any standard conversion sequence not containing an lvalue-to-rvalue, array-to-pointer, function-to-pointer, null pointer, null member pointer, boolean, or function pointer 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:

struct B { };
struct D : private B { };
void f() {
  static_cast<D*>((B*)0);               // error: B is a private base of D
  static_cast<int B::*>((int D::*)0);   // error: B is a private base of D
}

end example]

The lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions are applied to the operand. Such a static_­cast is subject to the restriction that the explicit conversion does not cast away constness, and the following additional rules for specific cases:

A value of a scoped enumeration type can be explicitly converted to an integral type. When that type is cv bool, the resulting value is false if the original value is zero and true for all other values. For the remaining integral types, the value is unchanged if the original value can be represented by the specified type. Otherwise, the resulting value is unspecified. 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. The value is unchanged if the original value is within the range of the enumeration values ([dcl.enum]). 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 type “pointer to cv1 B”, where B is a class type, can be converted to a prvalue of type “pointer to cv2 D”, where D is a 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 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 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 of B of type cv2 T”, where B is a base class of D, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.72 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. The null member pointer value is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the behavior is undefined. [Note: 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 address A of a byte in memory and A does not satisfy the alignment requirement of T, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion. [Example:

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]

Function types (including those used in pointer to member function types) are never cv-qualified; see [dcl.fct].