Реализация шаблона посетителя с использованием шаблонов С++

Я пытаюсь уменьшить количество шаблонов в моем коде, используя шаблоны С++ для реализации шаблона посетителя. До сих пор я придумал это:

class BaseVisitor {
public:
    virtual ~BaseVisitor() {}
};

template<typename T>
class Visitor : public BaseVisitor {
public:
    virtual void visit(T& /* visitable */) = 0;
};

template<typename Derived>
class Visitable {
public:
    void accept(Visitor<Derived>& visitor) {
        visitor.visit(static_cast<Derived&>(*this));
    }
};

И каждый подкласс Visitable выглядит следующим образом:

class Mesh : public Object, public Visitable<Mesh> {};
class Text : public Object, public Visitable<Text> {};

И, наконец, посетитель выглядит так:

class Renderer : public Visitor<Mesh>, public Visitor<Text> {}

До сих пор так хорошо... теперь вот проблема:

for(Scene::iterator it = scene.begin(); it != scene.end(); ++it) {
    Object& object = static_cast<Object&>(*it);
    if(pre_visit(object)) {
        object.accept(this); ///Erm, what do I cast to??
        post_visit(object);
    }
}

Мне нужно каким-то образом придать Visitable, чтобы я мог вызвать accept(), но, очевидно, я не знаю, что такое T. В качестве альтернативы я не могу добавить виртуальный accept() к Visitable-шаблону, потому что я не знаю, какой аргумент он должен принять.

Любой С++ Templating guru там знает, как сделать эту работу?

Ответы

Ответ 1

Это можно сделать в С++ 11 с использованием вариативных шаблонов. Продолжая от Пита ответ:

// Visitor template declaration
template<typename... Types>
class Visitor;

// specialization for single type    
template<typename T>
class Visitor<T> {
public:
    virtual void visit(T & visitable) = 0;
};

// specialization for multiple types
template<typename T, typename... Types>
class Visitor<T, Types...> : public Visitor<Types...> {
public:
    // promote the function(s) from the base class
    using Visitor<Types...>::visit;

    virtual void visit(T & visitable) = 0;
};

template<typename... Types>
class Visitable {
public:
    virtual void accept(Visitor<Types...>& visitor) = 0;
};

template<typename Derived, typename... Types>
class VisitableImpl : public Visitable<Types...> {
public:
    virtual void accept(Visitor<Types...>& visitor) {
        visitor.visit(static_cast<Derived&>(*this));
    }
};

Подклассы Visitable:

class Mesh : public Object, public VisitableImpl<Mesh, Mesh, Text> {};
class Text : public Object, public VisitableImpl<Text, Mesh, Text> {};
Подкласс

A Visitor:

class Renderer : public Visitor<Mesh, Text> {};

Непонятно, что такое value_type вашего контейнера Scene, но вам нужно получить ссылку или указатель на Visitable<Mesh, Text>, на который вызывать accept:

for(Scene::iterator it = scene.begin(); it != scene.end(); ++it) {
    Visitable<Mesh, Text>& object = static_cast<Visitable<Mesh, Text>&>(*it);
    if(pre_visit(object)) {
        object.accept(*this);
        post_visit(object);
    }
}

Ответ 2

Ваш BaseVisitor ничего не делает для вас, кроме как разрешать произвольным посетителям удалять посетителя. Вместо этого вы хотите иметь базовый класс для посетителя, который предоставляет все различные функции accept, которые могут быть вызваны на нем, а для Visitable - для этого посетителя.

Чтобы сделать это, вы можете использовать список типов , чтобы определить типы, которые может принять посетитель, иметь базовый класс visitee, который принимает тип список и добавьте список типов в качестве параметра в вашу реализацию visitee.

эскиз примера:

// assuming a typelist has typedefs first and second and a 
// type 'empty' representing end of type list

template<typename Types>
class Visitor : public Visitor<Types::second> {
public:
    // visitor has a visit function for each type in Types
    virtual void visit(typename Types::first& visitable) = 0;
};

template<> class Visitor<empty> { };

template<typename Types>
class Visitable{
    public:
    // base accepts a visitor which can visit any type in Types
    virtual void accept(Visitor<Types>& visitor) = 0;
};

template<typename Derived, typename Types>
class VisitableImpl : public Visitable<Types> {
public:
    // impl calls specific visit function 
    virtual void accept(Visitor<Types>& visitor) override {
        visitor.visit(static_cast<Derived&>(*this));
    }
};