需要一个虚拟模板成员的解决方法

23

我需要编写一个实现访问者设计模式的程序。问题在于基础访问者类是一个模板类。这意味着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();
}

2
无法编译。https://dev59.com/rXE95IYBdhLWcg3wV8a_ - yurib
2
编译器不接受虚函数中的模板。 - Puppy
1
为什么要将访问者变成模板?你想用这种结构解决什么问题?你所想做的事情无法完成,但可能有其他实现你真正需求的方式。 - David Rodríguez - dribeas
3个回答

18

你应该把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的派生类也是模板类,并且传递它们正确的类型/重载到visit中,那么你已经死了。


4
基本上,这就是 类型擦除 - sbi

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

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

2

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

使用常见的vtable实现,问题在于编译器需要为虚函数预留的条目数量未定义(该类型的不同实例化有多少个?),它们的顺序也是未定义的。如果声明类:

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

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

3
我知道这是不可能的,而且我也明白原因,我的问题是如何找到一个不涉及模板虚函数的解决方案。不管怎样,谢谢。 - yurib
@Yurib:你想要一个解决方案,但你还没有说明你的问题 - 我在评论中要求了这个问题:你真正想要实现什么。你只是询问了一个不起作用的潜在解决方案,而没有提到原始问题是什么。 - David Rodríguez - dribeas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接