Checking whether a cross-cast could possibly work?

2.4k views Asked by At

I know that it's legal to use dynamic_cast to do a "cross-cast" across a class hierarchy. For example, if I have classes that look like this:

  A   B
   \ /
    C

If I have an A* pointer that's pointing at an object of type C, then I can use

A* aPtr = /* ... something that produces a C* ... */
B* bPtr = dynamic_cast<B*>(aPtr);

to get a pointer to the B base object of the C I'm pointing at.

The reason I mention this is that at the time that I write the above code, it's possible that the compiler has not yet seen the definition of C even though it's seen A and B. This means that it's possible that the compiler does not detect any sort of connection between A and B, but it still has to compile the code anyway because it's possible for a class like C to exist and for the dynamic_cast to succeed under some circumstance.

The problem is that this means that I can accidentally cross-cast to an object of the wrong type. Suppose that I have classes that look like this:

A   B    D
 \ /   
  C

Here, D is some random unrelated class. If I try writing something like this:

A* aPtr = /* ... get a C* pointer ... */
D* dPtr = dynamic_cast<D*>(aPtr);

Then this dynamic_cast will always fail at runtime, since there's no possible way to connect A and D. If I'm using D accidentally because I meant to use B, the compiler will give me no indication whatsoever that I have a meaningless cast.

My question is: is there some way that I can get the compiler to warn me that the cast will always fail at runtime? I'd be happy with a language-level solution or some compiler setting for any major compiler that could detect this. If there's an external tool, that's fine as well; I just want to know if it's possible to catch this class of errors.

3

There are 3 answers

2
Ben Voigt On BEST ANSWER

It's not possible to detect this at compile-time. The class C that introduces the relationship could be found in a dynamically loadable library that hasn't even been written yet, and the compiler can't prove otherwise.

There may be a few exceptions though. If A has only private constructors (or a private destructor) then the compiler can be certain that there will be no new subclasses that aren't named as friends by A.

1
sharptooth On

Truth is when you use dynamic_cast you can cast any polymorphic type pointer to any polymorphic type pointer even when classes are unrelated to each other.

If you want compile-time check you need to use other casts - static_cast or implicit conversions - that might not suit for some other reasons in many cases.

The solution we use when dynamic_cast is needed is a templated function that does dynamic_cast and throws an exception if a null pointer is obtained. That's of course a runtime check only, but it's rather reliable.

0
Paweł On

That's the whole meaning of the dynamic cast: It is dynamic i.e. it is checked at the runtime not at the compilation time.

The compiler only checks if the casted classes are polymorphic. If the classes are not polymorphic then there will not be enough information to check the casting at the runtime.

And finally after the dynamic cast the program should check if the received pointer is not null. If you cast to a reference then you should catch std::bad_cast.