This is somewhat hypothetical as I'm not too worried about performance - just wondering which option is actually the fastest/most efficient in general, or if there is no difference whatsoever.
Suppose I have the following code for a visitor template that supports overloading:
#define IMPLEMENT_VISITOR_WITH_SUPERCLASS(superclass) \
typedef superclass visitor_super_t; \
virtual void visit(Visitor& v) { v.visit(*this); }
//-----------------------------------------------------------------------------
// Implementation detail:
// Selective dispatcher for the visitor - required to handle overloading.
//
template <typename T>
struct VisitorDispatch {
static void dispatch(Visitor* v, T* t) { v->visit(*t); }
};
// Specalization for cases where dispatch is not defined
template <> struct VisitorDispatch<void> {
static void dispatch(Visitor* v, void* t) { throw std::bad_cast(""); }
};
//-----------------------------------------------------------------------------
// Derive visitors from this and 'Visitor'.
template <typename T>
class VTarget
{
public:
// Don't really need a virtual dtor.
virtual void dispatch(T& t) = 0;
};
//-----------------------------------------------------------------------------
class Visitor
{
public:
virtual ~Visitor() = 0;
template <typename T>
void visit(T& t) {
typedef VTarget<T> target_t;
target_t* tgt = dynamic_cast<target_t*>(this);
if (tgt) {
tgt->dispatch(t);
}
else {
// Navigate up inhertiance hierarchy.
// requires 'super' to be defined in all classes in hierarchy
// applicable to this visitor.
typedef typename T::visitor_super_t super;
super* s = static_cast<super*>(&t);
VisitorDispatch<super>::dispatch(this, s);
}
}
};
//-----------------------------------------------------------------------------
inline Visitor::~Visitor() {}
This is then used to create generic visitors:
class CommonBase {
IMPLEMENT_VISITOR_WITH_SUPERCLASS(void)
virtual ~CommonBase() = 0;
};
class A : public CommonBase {
IMPLEMENT_VISITOR_WITH_SUPERCLASS(CommonBase)
};
class B : public CommonBase {
IMPLEMENT_VISITOR_WITH_SUPERCLASS(CommonBase)
};
class MyVisitor
: public Visitor
, public VTarget<CommonBase>
, public VTarget<A>
, public VTarget<B>
{
public:
virtual void dispatch(CommonBase& obj);
virtual void dispatch(A& obj);
virtual void dispatch(B& obj);
};
Using the visitor ultimately results in dynamic_cast<>
's from Visitor
to VTarget<T>
, which is a cross-cast.
The other way this could be implemented is to make Visitor
a virtual base of VTarget<T>
- MyVisitor
would then not need to inherit directly from Visitor anymore.
The dynamic_cast<>
in the Visitor::visit code would then result in a down-cast from the virtual base, Visitor
.
Is one method faster than the other when performing the casts? Or do you only get a size penalty for having the virtual base?
Well, it looks like the cross-cast method is faster than the virtual base method.
With a visitation that requires 1 fallback to a superclass, over 100000000 iterations, the cross-cast method took 30.2747 seconds, and the virtual base method took 41.3999 - about 37% slower.
With no fallback to an overload for a superclass, the cross-cast was 10.733 seconds and the virtual base 19.9982 (86% slower).
I was more interested to know how the dynamic_cast would operate in either mode, really.