This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 114a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-04-18


242. Interpretation of old-style casts

Section: 7.6.3  [expr.cast]     Status: CD4     Submitter: Mike Miller     Date: 30 Aug 2000

[Adopted at the February, 2016 meeting.]

The meaning of an old-style cast is described in terms of const_cast, static_cast, and reinterpret_cast in 7.6.3 [expr.cast] paragraph 5. Ignoring const_cast for the moment, it basically says that if the conversion performed by a given old-style cast is one of those performed by static_cast, the conversion is interpreted as if it were a static_cast; otherwise, it's interpreted as if it were a reinterpret_cast, if possible. The following example is given in illustration:

    struct A {};
    struct I1 : A {};
    struct I2 : A {};
    struct D : I1, I2 {};
    A *foo( D *p ) {
	return (A*)( p ); // ill-formed static_cast interpretation
    }

The obvious intent here is that a derived-to-base pointer conversion is one of the conversions that can be performed using static_cast, so (A*)(p) is equivalent to static_cast<A*>(p), which is ill-formed because of the ambiguity.

Unfortunately, the description of static_cast in 7.6.1.9 [expr.static.cast] does NOT support this interpretation. The problem is in the way 7.6.1.9 [expr.static.cast] lists the kinds of casts that can be performed using static_cast. Rather than saying something like "All standard conversions can be performed using static_cast," it says

An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration "T t(e);" is well-formed, for some invented temporary variable t.

Given the declarations above, the hypothetical declaration

    A* t(p);

is NOT well-formed, because of the ambiguity. Therefore the old-style cast (A*)(p) is NOT one of the conversions that can be performed using static_cast, and (A*)(p) is equivalent to reinterpret_cast<A*>(p), which is well-formed under 7.6.1.10 [expr.reinterpret.cast] paragraph 7.

Other situations besides ambiguity which might raise similar questions include access violations, casting from virtual base to derived, and casting pointers-to-members when virtual inheritance is involved.

Proposed resolution (October, 2015):

  1. Change 7.6.1.9 [expr.static.cast] paragraph 2 as follows:

  2. 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 ( 11.7 [class.derived]) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (7.3.12 [conv.ptr]), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and. If B is neither a virtual base class of D nor 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 (7.3.12 [conv.ptr]), the program is ill-formed. The result has type “cv2 D”. An xvalue of type “cv1 Bmay 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 subobject of an object of type D, the result refers to the enclosing object of type D. Otherwise, the behavior is undefined. [Example:...
  3. Change 7.6.1.9 [expr.static.cast] paragraph 4 as follows:

  4. An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (9.4 [dcl.init]) there is an implicit conversion sequence (12.2.4.2 [over.best.ics]) from e to T, or if overload resolution for a direct-initialization (9.4 [dcl.init]) of an object or reference of type T from e would find at least one viable function (12.2.3 [over.match.viable]). The effect of such an explicit conversion is the same as performing the declaration and initialization

      T t(e);
    

    for some invented temporary variable t (9.4 [dcl.init]) and then using the temporary variable as the result of the conversion. [Note: The conversion is ill-formed when attempting to convert an expression of class type to an inaccessible or ambiguous base class. —end note] The expression e is used as a glvalue if and only if the initialization uses it as a glvalue.

  5. Change 7.6.1.9 [expr.static.cast] paragraph 11 as follows:

  6. 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 (11.7 [class.derived]) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (7.3.12 [conv.ptr]), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and. If B is neither a virtual base class of D nor 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 (7.3.12 [conv.ptr]), the program is ill-formed. The null pointer value (7.3.12 [conv.ptr]) 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.
  7. Change 7.6.1.9 [expr.static.cast] paragraph 12 as follows:

  8. 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 ( 11.7 [class.derived]) of D, if a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (7.3.13 [conv.mem]), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.70 If no valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (7.3.13 [conv.mem]), the program is ill-formed. The null member pointer value (7.3.13 [conv.mem]) 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 7.6.4 [expr.mptr.oper]. —end note]