【问题标题】:Implementing the visitor pattern using C++ Templates使用 C++ 模板实现访问者模式
【发布时间】:2023-03-28 11:48:02
【问题描述】:

我一直在尝试通过使用 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++ 模板大师知道如何完成这项工作吗?

【问题讨论】:

标签: c++ templates visitor-pattern


【解决方案1】:

这可以在 C++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> {};

Visitor 子类:

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

尚不清楚您的Scene 容器的value_type 是什么,但您需要获取指向Visitable&lt;Mesh, Text&gt; 的引用或指针,以调用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);
    }
}

【讨论】:

  • 但这只有在Visitor 以完全相同的顺序支持VisitableImpl 特化中指定的所有类时才有效,不是吗?
【解决方案2】:

除了允许任意访问者删除访问者之外,您的 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));
    }
};

【讨论】:

    【解决方案3】:

    我还需要一个模板化的访问者模式,并且能够创建一个不涉及使用可变参数类型或类型列表的解决方案。

    // forward declarations for our Visitable interface
    class Object;
    class Visitor;
    
    // Visitable objects can accept a visitor.
    class Visitable
    {
    public:
        virtual ~Visitable() { }
        virtual void accept_visitor(Visitor& visitor) = 0;
        virtual void accept(Object& obj);
    };
    
    // A base class, to allow downcasting
    class Object
    {
    protected:
        virtual void _f() { }
    };
    
    // Our Visitor class, which will wrap our concrete visitor implementation
    class Visitor
    {
    public:
        Visitor(Object* obj);
    
        // Base class for concrete visitors
        template<typename D, typename V>
        class OfType : public Object
        {
        public:
            void visit(V* visitable) {
                D* derived = static_cast<D*>(this);
    
                // "duck-typed" method; if our derived class does not have
                // this method, compilation will fail.
                derived->on_visit(visitable);
            }
        };
    
        template<typename D, typename V>
        void visit(V* visitable);
    
    private:
        Object* m_obj;
    };
    
    Visitor::Visitor(Object* obj) : m_obj(obj) { }
    
    template<typename D, typename V>
    void Visitor::visit(V* visitable) {
        // check if our visitor is able to visit this instance
        OfType<D,V>* visitor = dynamic_cast<OfType<D,V>* >(m_obj);
        if (visitor) {
            visitor->visit(visitable);
        }
    }
    
    void Visitable::accept(Object& visitor) {
        Visitor wrapped(&visitor);
        accept_visitor(wrapped);
    }
    

    上面的接口定义好后,为可访问对象的访问者创建具体的接口,然后在你的具体类中实现它们:

    class This;
    
    class ThisVisitor : public Visitor::OfType<ThisVisitor, This>
    {
    public:
        virtual void on_visit(This* item) = 0;
    };
    
    class This : public Visitable
    {
    public:
        void accept_visitor(Visitor& visitor) {
            visitor.visit<ThisVisitor>(this);
        }
    };
    
    class That;
    
    class ThatVisitor : public Visitor::OfType<ThatVisitor, That>
    {
    public:
        virtual void on_visit(That* item) = 0;
    };
    
    class That : public Visitable
    {
    public:
        void accept_visitor(Visitor& visitor) {
            visitor.visit<ThatVisitor>(this);
        }
    };
    
    class MyVisitor : public ThisVisitor, public ThatVisitor
    {
    public:
        void on_visit(This* item) { printf("This!"); }
        void on_visit(That* item) { printf("That!"); }
    };
    
    int main(int argc, const char* argv[] {
        This item1;
        That item2;
        MyVisitor visitor;
        item1.accept(visitor);   // "This!"
        item2.accept(visitor);   // "That!"
    }
    

    您也可以完全跳过访问者界面并让您的具体访问者直接从 OfType&lt;Derived, SomeClass&gt; 派生,但我发现使用前者更好地扩展您的访问者,因为定义了新类(That 不应该关心谁访问只要它的类型是ThatVisitor)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-25
      • 1970-01-01
      • 2022-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-25
      • 2013-10-28
      相关资源
      最近更新 更多