I have stepped onto code like this:
dynamic_cast<A*>(p)->foo();
which is of course terrible, since when dynamic_cast returns 0, you have an undefined behavior.
Then I thought, one terrible surprise that it could bring, is that since when p can be cast to a A* it is the same as doing a static_cast, and when it cannot you get undefined behavior, the compiler could change the dynamic_cast into a static_cast and keep a conforming behavior. That said, I tried the following code with compiler explorer:
class A {
virtual void bar() = 0;
};
class B final: public A {
public:
void foo();
void bar() override;
};
void h();
void f(A* const p)
{
dynamic_cast<B*>(p)->foo();
}
and to my surprise every compiler kept the dynamic_cast call. Is there something that eluded me or do compilers simply do not do a possible optimization?
I don't see why the optimisation wouldn't be possible. The compiler can see that only two cases exist:
B
, so the cast can be staticA
, in which case the result is nullptr
and the full expression has undefined behaviour(No types deriving from B
are possible, due to the final
specifier. This is convenient because, if they were possible, a static cast would not necessarily be an adequate substitution for the dynamic cast — multiple inheritance and sidecasts would have to be considered, and there may not be sufficient information in this translation unit to do so. Link-time optimisation would further mitigate that, but in practice the majority of optimisations like this happen at compile-time, and any platform supporting things like dlopen
would also veto the possibility.)
So, we have only one case that results in a well-defined program.
Since compilers are permitted to assume that the input does not have undefined behaviour, if removing all code paths that result in UB leaves you with just one possible outcome, then the compiler can just assume that'll always be the outcome. That's the core reason for undefined behaviour to be a thing, to permit optimisations like this, and compilers perform them all the time to make your code nice and fast.
I'll admit to being slightly surprised that the mainstream compilers don't make use of that opportunity in this case. I'd at least expect a warning about a redundant dynamic_cast
; still, it's possible that some static analysers give you this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With