The "runtime overhead" of virtual dispatch is fairly low and not likely to matter except in tight loops:
- Lookup of the correct function
- Indirect call to function using its address
- Pipeline stall if the CPU branch predictor mispredicted the call
dynamic_cast
adds some additional overhead:
- Walking the runtime representation of type inheritance to get the correct adjustment factor for the object pointer (or fail). This still is quite fast if the inheritance graph is not deep.
The larger cost is that of missed optimization opportunities at compile-time. In C++ in particular, using compile-time polymorphism (templates) very often results in a lot of inlining, elimination of loops, precomputation, etc. When run-time polymorphism is in play, these optimizations are generally not possible.
Interestingly, dynamic_cast
is less of an obstacle to compile-time optimization because the compiler need only consider the two cases -- cast succeeds, and cast fails, which the programmer writes as two separate code paths both subject to optimization. In contrast, the "recommended" runtime polymorphism approach of using virtual calls is far more difficult to optimize because the set of possible types is open-ended.
Virtual calls are generally preferred over dynamic_cast
(or tag-and-switch) for ease of maintenance (as @user4581301 comments), with performance considerations secondary.