2012-08-03 100 views
15

我一直试图通过使用C++模板来实现访问者模式来减少我的代码中样板的数量。到目前为止,我想出了这一点:使用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)); 
    } 
}; 

而且的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模板,因为我不知道应该采用什么参数。

任何C++模板大师的外面知道如何使这项工作?

+1

下面是做到这一点的目前现代的方式:参观 - 无需行进(https://backwardsincompatibilities.wordpress.com/2015/10/26/visiting-without-travelling/)。 – davidhigh 2016-02-14 00:49:39

回答

20

这可以在C++ 11个使用可变参数模板来完成。

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

子类:从皮特的回答继续

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

一个Visitor子类:

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

目前尚不清楚你Scene容器的value_type是什么,但你需要获得要致电accept的参考或指向Visitable<Mesh, Text>的指针:

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

但是这只有在接受时才有效'Visitor'以完全相同的顺序支持'VisitableImpl'专业化中指定的所有类,不是吗? – grinsekrokodil 2017-01-25 17:04:37

5

您的BaseVisitor不会为您做任何事情,除了允许任意访问者删除访问者。相反,您希望为访问者提供一个基类,它可提供所有不同的accept函数可供调用,并且Visitable可接受此访问者。

要做到这一点,您可以使用type list来定义访问者可以接受的类型,拥有一个接受类型列表的基本可访问者类,并将类型列表作为参数添加到您的受保护实现中。

草图示例的:

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