10
votes

I give up on this...

$5.2.7/2- "If T is a pointer type, v shall be an rvalue of a pointer to complete class type, and the result is an rvalue of type T. If T is a 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."

In accordance with the above, the following code should be well-formed.

struct A{};
struct B : A{};

int main(){
   B b;
   A a, &ar1 = b;

   B& rb1 = dynamic_cast<B&>(ar1);  // Does not $5.2.7/2 apply here?
   B& rb2 = dynamic_cast<B&>(a);    // and also here?
}

But it is not. All compilers complain about the operand to dynamic_cast not being polymorphic in accordance with

$5.2.7/6- Otherwise, v shall be a pointer to or an lvalue of a polymorphic type (10.3).

So my question is what does $5.2.7/2 mean? Why does $5.2.7/6 kick in here?

3

3 Answers

9
votes

Well, all requirements in 5.2.7 should be observed together. You can't just stop after 5.2.7/2 and start writing code that supposedly satisfies everything "up to 5.2.7/2". The entire 5.2.7 defines the specification of dynamic_cast.

The polymorphic requirement is singled out because it is conditional. When you use dynamic_cast for upcasts, the polymorphic requirement does not apply (in fact, dynamic_cast is equivalent to static_cast in upcasts). The polymorphic requirement only applies when you use dynamic_cast for downcasts or crosscasts.

The specification of dynamic_cast is organized sequentially, meaning that it takes care of simpler cases first, and then proceeds to more complicated applications. You are supposed to read it step by step, until it covers your specific situation. Everything you read along that path applies cumulatively, And "otherwise" means: "if we haven't covered your case yet, then continue reading".

4
votes

In order to do a downcast as in your example, Struct A needs to be polymorphic, and have RTTI. Here's an adjusted version that works, to a point:

struct A{virtual void f(){}};
struct B : A{};

int main(){
   B b;
   A a, &ar1 = b;

   B& rb1 = dynamic_cast<B&>(ar1);  // Does not $5.2.7/2 apply here?
   //B& rb2 = dynamic_cast<B&>(a);    // and also here?
}

By adding a virtual making it polymorphic, RTTI is enabled for the class, allowing downcasts.

Note that your second example cannot work - since you are casting a pod (a) to a reference to a pod - which is not allowed.


Update:

Your code is not allowed under 5.2.7.5 and is neither allowed under 5.2.7.6. My adjustment makes it work under 5.2.7.6

3
votes

"Otherwise" in this case means, "unless the conditions in 5.2.7/5 apply".

You can tell this because /2 places a requirement on the program regarding the operand of the the dynamic_cast (note the "shall" language of "v shall be an lvalue" vs. the "is" language of "the result is an lvalue"). In common with other places in the standard, expressing a requirement doesn't necessarily mean that it's the only requirement. Other clauses can state extra requirements. In this case, /6 states an extra requirement that only applies in certain cases, depending on T and the static type of v.

/3, /4, /5 are telling you about the value of the result, and they're entirely consistent with the requirement in /2. None of them starts with "Otherwise". So to me it's fairly obvious that they do not form an "else if" chain starting at /2.

Some brackets or something might make this clearer (i.e. that the "otherwise" in /6 applies to the "if" in /5, and not to the "if" in /2, /3, or /4). But that's just not the house style.

Aside from anything else, the "otherwise" in /5 logically cannot meaningfully apply to the conditions in /2. /1 says that T must be "pointer or reference to complete class type, or cv void* ". /2 covers two cases - pointer types, and reference types. That's everything. There is no "otherwise" to /2 (unless it were to say "otherwise, a conforming compiler must issue a diagnostic", but that's implicit)