使用C++模板实现访问者模式

Kaz*_*ade 20 c++ templates visitor

我一直在尝试通过使用C++模板来实现访问者模式来减少代码中的样板量.到目前为止,我已经想出了这个:

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));
    }
};
Run Code Online (Sandbox Code Playgroud)

Visitable的每个子类都如下所示:

class Mesh : public Object, public Visitable<Mesh> {};
class Text : public Object, public Visitable<Text> {};
Run Code Online (Sandbox Code Playgroud)

最后,访客看起来像这样:

class Renderer : public Visitor<Mesh>, public Visitor<Text> {}
Run Code Online (Sandbox Code Playgroud)

到目前为止一切都很好......现在问题在于:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我需要以某种方式转换为Visitable以便我可以调用accept(),但显然我不知道T是什么.或者,我无法向Visitable模板添加虚拟accept(),因为我不知道它应该采用什么参数.

任何C++ Templating大师都知道如何使这项工作?

And*_*ard 22

这可以使用可变参数模板在C++ 11中完成.继续Pete的回答:

// 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));
    }
};
Run Code Online (Sandbox Code Playgroud)

子类Visitable:

class Mesh : public Object, public VisitableImpl<Mesh, Mesh, Text> {};
class Text : public Object, public VisitableImpl<Text, Mesh, Text> {};
Run Code Online (Sandbox Code Playgroud)

一个Visitor子类:

class Renderer : public Visitor<Mesh, Text> {};
Run Code Online (Sandbox Code Playgroud)

目前还不清楚的是什么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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 但这仅在接受的“Visitor”以完全相同的顺序支持“VisitableImpl”专业化中指定的所有类时才有效,不是吗? (2认同)

Pet*_*ham 5

除了允许任意访问者删除访问者之外,您的BaseVisitor不会为您做任何事情.相反,您希望为访问者提供一个基类,它提供可以在其上调用的所有不同accept功能,以及Visitable接受此访问者.

为此,您可以使用类型列表来定义访问者可以接受的类型,具有获取类型列表的基本访问者类,并将类型列表作为参数添加到您的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));
    }
};
Run Code Online (Sandbox Code Playgroud)