22

我需要编写一个实现访问者设计模式的程序。问题是基本访问者类是一个模板类。这意味着 BaseVisited::accept() 将模板类作为参数,并且由于它使用“this”并且我需要“this”来指向对象的正确运行时实例,因此它也需要是虚拟的。
我想知道有没有办法解决这个问题。

template <typename T>
class BaseVisitor {
  public:
    BaseVisitor();
    T visit(BaseVisited *visited);
    virtual ~BaseVisitor();
}


class BaseVisited {
  BaseVisited();
  template <typename T>
    virtual void accept(BaseVisitor<T> *visitor) { visitor->visit(this); }; // problem
  virtual ~BaseVisited();
}
4

3 回答 3

17

您应该做的是分离 BaseVisitor。

class BaseVisited;
class BaseVisitorInternal {
public:
    virtual void visit(BaseVisited*) = 0;
    virtual ~BaseVisitorInternal() {}
};
class BaseVisited {
    BaseVisited();
    virtual void accept(BaseVisitorInternal* visitor) { visitor->visit(this); }
};
template<typename T> class BaseVisitor : public BaseVisitorInternal {
    void visit(BaseVisited* visited);
};

如果您也需要对 BaseVisited 的派生类进行模板化并传递它们正确的类型/重载来访问,那么您就正式死了。

于 2010-05-30T19:00:18.183 回答
4

我想出了一些与 DeadMG 稍有不同的东西:

class BaseVisited;

class IVisitor {
  public:
    virtual void visit(BaseVisited *visited) = 0;
    virtual ~IVisitor();
};

template <typename T>
class BaseVisitor : public IVisitor {
  public:
    BaseVisitor();
    virtual void visit(BaseVisited *visited);
    virtual ~BaseVisitor();
    virtual T result();
};


class BaseVisited {
  public:
    BaseVisited();
    virtual void accept(IVisitor *visitor) { visitor->visit(this); };
    virtual ~BaseVisited();
};

Mine 有一个额外的result()成员函数,可让您检索上次访问的结果。

于 2010-05-30T19:05:50.897 回答
3

您不能声明/定义模板化的虚函数。原因是编译器看到基类定义时必须知道虚拟调度机制,但模板是按需编译的。

对于常见的 vtable 实现,问题在于编译器必须为虚函数保留的条目数是未定义的(可以有多少种不同的类型实例化?),它们的顺序也是如此。如果你声明类:

class base {
public:
   virtual void foo();
   virtual int bar();
};

编译器可以在 vtable 中为指向foobar在 vtable 中的指针保留两个条目,并且只需检查类定义即可完美定义 vtable。这是模板化函数无法实现的。

于 2010-05-30T19:31:05.650 回答