I saw one book on C++ mentioning that navigating inheritance hierarchies using static cast is more efficient than using dynamic cast.
Example:
#include <iostream>
#include <typeinfo>
using namespace std;
class Shape { public: virtual ~Shape() {}; };
class Circle : public Shape {};
class Square : public Shape {};
class Other {};
int main() {
Circle c;
Shape* s = &c; // Upcast: normal and OK
// More explicit but unnecessary:
s = static_cast<Shape*>(&c);
// (Since upcasting is such a safe and common
// operation, the cast becomes cluttering)
Circle* cp = 0;
Square* sp = 0;
// Static Navigation of class hierarchies
// requires extra type information:
if(typeid(s) == typeid(cp)) // C++ RTTI
cp = static_cast<Circle*>(s);
if(typeid(s) == typeid(sp))
sp = static_cast<Square*>(s);
if(cp != 0)
cout << "It's a circle!" << endl;
if(sp != 0)
cout << "It's a square!" << endl;
// Static navigation is ONLY an efficiency hack;
// dynamic_cast is always safer. However:
// Other* op = static_cast<Other*>(s);
// Conveniently gives an error message, while
Other* op2 = (Other*)s;
// does not
} ///:~
However, both dynamic cast and static cast (as implemented above) need RTTI enabled for such navigation to work. It's just that dynamic cast requires the class hierarchy to be polymorphic (i.e. base class having at least one virtual function).
Where does this efficiency gain for static cast come from?
The book does mention that dynamic cast is the preferred way for doing type-safe downcasting.
static_cast
per se DOESN'T need RTTI -- typeid
does (as does dynamic_cast
), but that's a completely different issue. Most casts are just telling the compiler "trust me, I know what I'm doing" -- dynamic_cast
is the exception, it asks the compiler to check at runtime and possibly fail. That's the big performance difference right there!
It's much better to avoid switching on types at all if possible. This is usually done by moving the relevant code to a virtual method that is implemented differently for different subtypes:
class Shape {
public:
virtual ~Shape() {};
virtual void announce() = 0; // And likewise redeclare in Circle and Square.
};
void Circle::announce() {
cout << "It's a circle!" << endl;
}
void Square::announce() {
cout << "It's a square!" << endl;
}
// Later...
s->announce();
If you are working with a pre-existing inheritance hierarchy that you can't change, investigate the Visitor pattern for a more extensible alternative to type-switching.
More info: static_cast
does not require RTTI, but a downcast using it can be unsafe, leading to undefined behaviour (e.g. crashing). dynamic_cast
is safe but slow, because it checks (and therefore requires) RTTI info. The old C-style cast is even more unsafe than static_cast
because it will quietly cast across completely unrelated types, where static_cast
would object with a compile-time error.
With the static cast (and typeid check) you cannot downcast to an intermediate type (child derives from father derives from grandfather, you cannot downcast from grandfather to father) the usage is a little more limited. static_cast without the typeid check is sacrificing correctness for perfomance, and then you know what they say:
He who sacrifices correctness for performance deserves neither
Then of course, there are situations where you are in desperate need of a few CPU instructions and there is nowhere else to look for improvements and you are actually safe on what you are doing and you have meassured (right?) that the only place to gain performance is using static_cast instead of dynamic_cast... then you know you must rework your design, or your algorithms or get better hardware.
The restrictions you impose by using rtti + static_cast is that you will not be able to extend your code with new derived classes at a later time without reworking all places where you have used this trick to gain just a few CPU instructions. That reworking itself will probably take more time (engineering time that is more expensive) than the CPU time you have obtained. If, at any rate, the time devoted to downcasts is noticeable, then rework your design as j_random_hacker suggests, it will improve both in design and performance.
dynamic_cast
would return NULL if you hadn't done the typeid check and the cast couldn't succeed. static_cast
would succeed (and lead to undefined behavior, such as an eventual crash). That's likely the speed difference.