dynamic_cast has a lot of SO questions here. I read only a few, and also do not often use this method in my own code, so my answer reflects my opinion on this issue, and not my experience. Beware.
(1.) Why is casting up / down considered a poor design besides the fact that it looks awful?
(3.) Are there any rules of thumb for how I can avoid a design like the one described above?
When reading the Stroustrup C ++ frequently asked questions, imo has one central message: do not trust people who never use any tool. Rather, use the right tool for this task.
Sometimes, however, two different tools can have a very similar purpose, and so it is. Basically you can recode any functionality using dynamic_cast functions via virtual .
So, when is dynamic_cast right tool? (see also. What is the correct use case for dynamic_cast? )
One of the possible situations is that you have a base class that you cannot extend, but nevertheless you need to write overloaded code. With dynamic casting, you can make it non-invasive.
Another is where you want to save the interface, i.e. pure virtual base class, and do not want to implement the corresponding virtual function in any derived class.
Often, however, you prefer to rely on a virtual function - if only on reduced ugliness. In the future, this is safer: dynamic casting may fail and terminate your program; a virtual function call (usually) will not.
In addition, implemented in terms of pure functions, you will not forget to update it in all the necessary places when adding a new derived class. On the other hand, dynamic typing can be easily forgotten in the code.
The virtual function version of your example
Here is another example:
Element* currentElement; void addToCurrentElement(const C *c) { if(A *a = dynamic_cast<A*>(currentElement)) {
To rewrite it, add (possibly pure) virtual functions add(A*) , add(B*) and add(C*) , which you overload in derived classes, in your database.
struct A : public Element { virtual add(A* c) { } virtual add(B* c) { } virtual add(C* c) { } };
and then call it in your function or maybe write a more concise function template
template<typename T> void addToCurrentElement(T const* t) { currentElement->add(t); }
I would say that this is a standard approach. As already mentioned, the drawback may be that for pure virtual functions you will need N*N overloads, where possibly N can be enough (say, if only A::add requires special processing).
Other alternatives may use RTTI , CRTP pattern, erase type, and possibly more.
(2.) Is slow_cast slow?
When considering most of the answers across the entire network state, yes, the dynamic effect seems slow, for example, here . However, I have no practical experience to support or cancel this expression.