In most cases the visitor-pattern can be used to avoid downcasts. It can be used to avoid dynamic_cast, too.
Some caveats:
1) It must be possible to change the offending classes.
2) You may need to know EVERY derived class.
3) The objects must be known to derive from at least the baseclass, you cannot try to cast completely unrelated types. (This seems to be fulfilled: "I want to downcast from the base class to the derived class")
In the following example i used templates. These can be easily get rid off, but would require quite some writing effort.
class A;
class B;
class C;
class D;
// completely abstract Visitor-baseclass.
// each visit-method must return whether it handled the object
class Visitor
{
public:
virtual bool visit(A&) = 0;
virtual bool visit(B&) = 0;
virtual bool visit(C&) = 0;
virtual bool visit(D&) = 0;
};
class A
{
public:
virtual const char* func() { return "A"; };
virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class B : public virtual A
{
public:
virtual const char* func() { return "B"; };
virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class C : public virtual A
{
public:
virtual const char* func() { return "C"; };
virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class D : public B, public C
{
public:
virtual const char* func() { return "D"; };
virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
// implementation-superclass for visitors:
// each visit-method is implemented and calls the visit-method with the parent-type(s)
class InheritanceVisitor : public Visitor
{
virtual bool visit(A& a) { return false; }
virtual bool visit(B& b) { return visit(static_cast<A&>(b)); }
virtual bool visit(C& c) { return visit(static_cast<A&>(c)); }
virtual bool visit(D& d) { return visit(static_cast<B&>(d)) || visit(static_cast<C&>(d)); }
};
template<typename T> // T must derive from A
class DerivedCastVisitor : public InheritanceVisitor
{
public:
DerivedCastVisitor(T*& casted) : m_casted(casted) {}
virtual bool visit(T& t)
{ m_casted = &t; return true; }
private:
T*& m_casted;
};
// If obj is derived from type T, then obj is casted to T* and returned.
// Else NULL is returned.
template<typename T>
T* derived_cast(A* obj)
{
T* t = NULL;
if (obj)
{
DerivedCastVisitor<T> visitor(t);
obj->accept(visitor);
}
return t;
}
int main(int argc, char** argv)
{
std::auto_ptr<A> a(new A);
std::auto_ptr<A> b(new B);
std::auto_ptr<A> c(new C);
std::auto_ptr<A> d(new D);
assert(derived_cast<A>(a.get()) != NULL); // a has exact type A
assert(derived_cast<B>(b.get()) != NULL); // b has exact type B
assert(derived_cast<A>(b.get()) != NULL); // b is derived of A
assert(derived_cast<C>(b.get()) == NULL); // b is not derived of C
assert(derived_cast<D>(d.get()) != NULL); // d has exact type D
assert(derived_cast<B>(d.get()) != NULL); // d is derived of B
assert(derived_cast<C>(d.get()) != NULL); // d is derived of C, too
assert(derived_cast<D>(c.get()) == NULL); // c is not derived of D
return 0;
}