Title
Problems in the specification of dynamic_cast
Status
cd2
Section
7.6.1.7 [expr.dynamic.cast]
Submitter
Daniel Krügler

Created on 2007-12-01.00:00:00 last changed 170 months ago

Messages

Date: 2009-03-15.00:00:00

[Voted into the WP at the March, 2009 meeting.]

Date: 2008-06-15.00:00:00

Proposed resolution (June, 2008):

  1. Change 7.6.1.7 [expr.dynamic.cast] paragraph 5 as follows:

  2. ...In both the pointer and reference cases, cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2, and B shall be an accessible unambiguous base class of D the program is ill-formed if cv2 is greater cv-qualification than cv1 or if B is an inaccessible or ambiguous base class of D.
  3. Change the comment in the example in 7.6.1.7 [expr.dynamic.cast] paragraph 9 as follows:

  4.     bp = dynamic_cast<B*>(&d);     // fails ill-formed (not a run-time check)
    
  5. Change 7.6.1.7 [expr.dynamic.cast] paragraph 8 as follows:

  6. The If C is the class type to which T points or refers, the run-time 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 a T C object, and if only one object of type T C is derived from the subobject pointed (referred) to by v the result is a pointer (an lvalue referring) to that T 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 T C, that is unambiguous and public, the result is a pointer (an lvalue referring) to the T C subobject of the most derived object.

    • Otherwise, the run-time check fails.

Date: 2007-12-01.00:00:00

At least one implementation accepts the following example as well-formed (returning a null pointer at runtime), although others reject it at compile time:

    struct A { virtual ~A(); };
    struct B: private A { } b;
    A* pa = dynamic_cast<A*>(&b);

Presumably the intent of 7.6.1.7 [expr.dynamic.cast] paragraph 5 is that all up-casts (converting from derived to base) are to be handled at compile time, regardless of whether the class involved is polymorphic or not:

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 B subobject of the D object pointed to by v. Similarly, if T is “reference to cv1 B” and v has type cv2 D such that B is a base class of D, the result is the unique B subobject of the D object referred to by v... In both the pointer and reference cases, cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2, and B shall be an accessible unambiguous base class of D.

One explanation for the implementation that accepts the example at compile time is that the final sentence is interpreted as part of the condition for the applicability of this paragraph, so that this case falls through into the description of runtime checking that follows. This (mis-)interpretation is buttressed by the example in paragraph 9, which reads in significant part:

    class A { virtual void f(); };
    class B { virtual void g(); };
    class D : public virtual A, private B {};
    void g() {
        D d;
        B* bp;
        bp = dynamic_cast<B*>(&d); // fails
    }

The “fails” comment is identical to the commentary on the lines in the example where the run-time check fails. If the interpretation that paragraph 5 is supposed to apply to all up-casts, presumably this comment should change to “ill-formed,” or the line should be removed from the example altogether.

It should be noted that this interpretation (that the example is ill-formed and the runtime check applies only to down-casts and cross-casts) rejects some programs that could plausibly be accepted and actually work at runtime. For example,

    struct B { virtual ~B(); };
    struct D: private virtual B { };

    void test(D* pd) {
        B* pb = dynamic_cast<B*>(pd); // #1
    }

    struct D2: virtual B, virtual D {};

    void demo() {
        D2 d2;
        B* pb = dynamic_cast<B*>(&d2); // #2
        test(&d2); // #3
    }

According to the interpretation that paragraph 5 applies, line #1 is ill-formed. However, converting from D2 to B (line #2) is well-formed; if the alternate interpretation were applied, the conversion in line #1 could succeed when applied to d2 (line #3).

One final note: the wording in 7.6.1.7 [expr.dynamic.cast] paragraph 8 is incorrect:

The run-time 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 a T object, and if only one object of type T is derived from the subobject pointed (referred) to by v the result is a pointer (an lvalue referring) to that T 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 T, that is unambiguous and public, the result is a pointer (an lvalue referring) to the T subobject of the most derived object.

  • Otherwise, the run-time check fails.

All uses of T in this paragraph treat it as if it were a class type; in fact, T is the type to which the expression is being cast and thus is either a pointer type or a reference type, not a class type.

History
Date User Action Args
2010-03-29 00:00:00adminsetstatus: wp -> cd2
2009-08-03 00:00:00adminsetstatus: dr -> wp
2009-03-23 00:00:00adminsetmessages: + msg2018
2009-03-23 00:00:00adminsetstatus: ready -> dr
2008-10-05 00:00:00adminsetstatus: review -> ready
2008-06-29 00:00:00adminsetmessages: + msg1695
2008-06-29 00:00:00adminsetstatus: drafting -> review
2008-03-17 00:00:00adminsetstatus: open -> drafting
2007-12-01 00:00:00admincreate