【问题标题】:Using the Visitor Pattern with template derived classes将访问者模式与模板派生类一起使用
【发布时间】:2011-10-24 13:14:18
【问题描述】:

我尝试使用模板派生类实现访问者模式

我使用 gcc 4.5

这里是 VisitorTemplate.hpp,我在 Visitor 类中专门 Derived,但我希望能够处理任何类型:

edit : 感谢 interjay 的建议,现在代码编译运行没有错误

#ifndef VISITORTEMPLATE_HPP_
#define VISITORTEMPLATE_HPP_

#include <iostream>
#include <string>
using namespace std;

template<class T> Derived;

class Visitor
{
  public:
    virtual void visit(Derived<string> *e) = 0;
};

class Base
{
  public:
    virtual void accept(class Visitor *v) = 0;
};

template<class T>
Derived: public Base
{
  public:
    virtual void accept(Visitor *v)
    {
       v->visit(this);
    }
    string display(T arg)
    {
       string s = "This is : " + to_string(arg);
       return s;
    }
};

class UpVisitor: public Visitor
{
   virtual void visit(Derived<string> *e)
   {
     cout << "do Up on " + e->display("test") << '\n';
   }
};

class DownVisitor: public Visitor
{
   virtual void visit(Derived<string> *e)
   {
     cout << "do Down on " + e->display("test") << '\n';
   }
};

#endif /* VISITORTEMPLATE_HPP_ */

main.cpp

Base* base = new Derived<string>();
Visitor* up = new UpVisitor();
Visitor* down = new DownVisitor();
base->accept(up);
base->accept(down);

现在我的目标是在访问中使用 Derived 而无需专门化;不幸的是,visit 是一个虚拟方法,所以我无法对其进行模板化

【问题讨论】:

  • 你用的是什么编译器?我刚刚将您的示例代码提供给 Visual C++ 2010,它编译得非常愉快,除了抱怨 std::to_string 无法处理模板参数参数类型。在您的示例代码中,我看不出它为什么会抱怨类型不完整的任何原因,因为您没有错过为单个纯虚函数提供实现。
  • @MatthewWalton:“我使用 gcc 4.5。”但是,指出编译器消息引用的行总是很好的。我不想在浏览器中计算 31 行。
  • @sbi doh,我一定忘记了当我为编译器提供代码时。不过,我认为特定类型的错误不会是 GCC 和 VC++ 不同意的类型。
  • 好的,忘记我的代码,你有办法处理这个通用的 Vistor 问题吗?
  • 有趣。似乎class 的前向声明与templates 并不完全一致

标签: c++ templates design-patterns c++11 visitor-pattern


【解决方案1】:

来自现代 C++ - 设计通用编程和应用设计模式 - Andrei Alexandrescu

#include <iostream>

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

template <class T, typename R = int>
class Visitor
{
    public:
        virtual R visit(T &) = 0;
};

template <typename R = int>
class BaseVisitable
{
    public:
        typedef R ReturnType;
        virtual ~BaseVisitable() {};
        virtual ReturnType accept(BaseVisitor & )
        {
            return ReturnType(0);
        }
    protected:
        template <class T>
        static ReturnType acceptVisitor(T &visited, BaseVisitor &visitor)
        {
            if (Visitor<T> *p = dynamic_cast< Visitor<T> *> (&visitor))
            {
                return p->visit(visited);
            }
            return ReturnType(-1);
        }

        #define VISITABLE() \
            virtual ReturnType accept(BaseVisitor &v) \
                { return acceptVisitor(*this, v); }
};


/** example of use */
class Visitable1 : public BaseVisitable<int>
{
    /* Visitable accept one BaseVisitor */
    public:
        VISITABLE();
};

class Visitable2 : public BaseVisitable<int>
{
    /* Visitable accept one BaseVisitor */
    public:
        VISITABLE();
};

class VisitorDerived : public BaseVisitor,
        public Visitor<Visitable1, int>,
        public Visitor<Visitable2, int>
{
    public:
        int visit(Visitable1 & c)
        {
            std::cout << __PRETTY_FUNCTION__ << std::endl;
        }
        int visit(Visitable2 & c)
        {
            std::cout << __PRETTY_FUNCTION__ << std::endl;
        }
};

int main(int argc, char **argv)
{
    VisitorDerived visitor;
    Visitable1 visitable1;
    Visitable2 visitable2;

    visitable1.accept(visitor);
    visitable2.accept(visitor);
}

可以避免使用 CRTP 模式的 dynamic_cast,例如:

#include <iostream>

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

template <class T>
class Visitor
{
    public:
        virtual void visit(T &) = 0;
};

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

/** example of use */
class Visitable1 : public BaseVisitable<Visitable1>
{
};

class Visitable2 : public BaseVisitable<Visitable2>
{
};

class VisitorDerived : public BaseVisitor, 
                       public Visitor<Visitable1>,
                       public Visitor<Visitable2>
{
    public:
        void visit(Visitable1 & c)
        {
            std::cout << __PRETTY_FUNCTION__ << std::endl;
        }
        void visit(Visitable2 & c)
        {
            std::cout << __PRETTY_FUNCTION__ << std::endl;
        }
};

int main(int argc, char **argv)
{
    VisitorDerived visitor;
    Visitable1 visitable1;
    Visitable2 visitable2;

    visitable1.accept<VisitorDerived>(visitor);
    visitable2.accept<VisitorDerived>(visitor);
}

【讨论】:

  • 我觉得有点复杂;是否可以避免 dynamic_cast ?
  • 是的,您可以使用 CRTP 模式,例如:
  • 你将如何扩展它以覆盖层次结构,例如DerivedVisitable1,同时保持static_cast?是否可以不更改为循环实现?
  • 如何更改此代码以允许使用 BaseVisitables 的容器。使用第一个示例,我可以有一个“std::vector”,但我不能使用第二个示例,因为 BaseVisitable 是模板化的。
【解决方案2】:

您的Derived 类不能使用Visitor,因为它尚未定义(它只是前向声明的,因此是不完整的类型)。

您可以通过将Visitor 定义放在Derived 之前来修复编译错误。您还需要在定义 Visitor 之前前向声明 Derived

template <class T> class Derived;

class Visitor {
public:
    virtual void visit(Derived<string> *e) = 0;
};

template <class T>
class Derived : public Base {
    //.... can call Visitor methods here ...
};

【讨论】:

    猜你喜欢
    • 2011-06-20
    • 2021-06-10
    • 1970-01-01
    • 2016-02-02
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2012-09-12
    • 2022-12-04
    相关资源
    最近更新 更多