Что быстрее: нисходящее из виртуальной базы или перекрестное приведение? - PullRequest
2 голосов
/ 04 августа 2011

Это несколько гипотетично, поскольку я не слишком беспокоюсь о производительности - просто задаюсь вопросом, какой вариант на самом деле самый быстрый / самый эффективный в целом, или нет никакой разницы.

Предположим, у меня есть следующеекод для шаблона посетителя, который поддерживает перегрузку:

#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() {}

Затем он используется для создания общих посетителей:

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);
};

Использование посетителя в конечном итоге приводит к dynamic_cast<> с Visitor to VTarget<T>, который является перекрестным приведением.

Другой способ, которым это может быть реализовано, состоит в том, чтобы сделать Visitor виртуальной базой VTarget<T> - MyVisitor, тогда не нужно будет наследовать напрямуюот посетителя больше.dynamic_cast<> в коде Visitor :: визит будет приводить к понижению от виртуальной базы, Visitor.

Является ли один метод быстрее, чем другой, при выполнении приведения?Или вы получаете только штраф за размер виртуальной базы?

1 Ответ

1 голос
/ 04 августа 2011

Ну, похоже, метод перекрестного преобразования быстрее, чем метод виртуальной базы.

При посещении, которое требует 1 откат к суперклассу, более 100000000 итераций, метод перекрестного преобразования занял 30,2747 секунд, а метод виртуальной базы - 41,3999 - примерно на 37% медленнее.

Без перегрузки для суперкласса кросс-бросок составил 10,733 секунды, а виртуальная база - 19,9982 (на 86% медленнее).

Мне было больше интересно узнать, как dynamic_cast будет работать в любом режиме, действительно.

...