问题是template <typename T> virtual void foo()
是非法的,我正在尝试使用访问者模式来解决这个问题(一般来说已知这种情况)。但是Base的派生类是模板类,现在我遇到了访问者类中的虚拟模板问题。如何解决这个问题?
struct Base {
// template <typename T> virtual void foo() = 0; // illegal
virtual void foo (class Visitor& visitor) = 0; // The attempted solution
};
template <typename D>
struct Derived : Base {
virtual void foo (Visitor&) override;
};
struct Visitor {
//template <typename D> // same problem again!
virtual void visit (Derived<D>*) const = 0;
};
template <typename T, typename D>
struct FooVisitor : Visitor {
virtual void visit (Derived<D>*) const override {/*Do whatever with T*/}
};
template <typename D>
void Derived<D>::foo (Visitor& visitor) {visitor.visit(this);}
对于所有解决方案,我们假设D应该有大约一百个值,并且不断引入新的D类。每个人都会以同样的方式使用D.为简单起见,我们假设每个访问函数都将使用D与
func<D>();
其中
template <typename D> void Base::func();
是Base中的一些辅助函数。
答案 0 :(得分:2)
这是一个可行的解决方案。请注意,此处的假设是您只使用正确的类型进行调用:
struct Base {
virtual void foo(struct Visitor& visitor) = 0;
};
template <typename D>
struct Derived : Base {
virtual void foo (Visitor&v) override;
};
struct Visitor {
virtual ~Visitor() {} // Make this class polymorphic.
};
template <typename D>
struct Visitor_tmpl : public Visitor {
virtual void visit (Derived<D>*) const {/*Do whatever with T*/}
};
template <typename T, typename D>
struct FooVisitor : Visitor_tmpl<D> {
virtual void visit (Derived<D>*) const override {/*Do whatever with T*/}
};
template <typename D>
void Derived<D>::foo(Visitor&v) {
// In this function, D has been bound now to a specific type, so we downcast.
// It will throw an exception if not the right type.
dynamic_cast<Visitor_tmpl<D> &>(v).visit(this);
}
int main() {
Derived<int> d;
FooVisitor<double, int> v;
d.foo(v);
}
答案 1 :(得分:0)
Jarod42提到了一种可能的解决方案,即指定所有可能出现的类型。但是,通常情况下,您希望给出一个标准实现,并且只在需要时重载此值。
Base*
编辑:这是使用基本double dispatch的另一种可能性:
struct Type1 {};
//...
struct TypeN {};
struct Visitor
{
virtual ~Visitor() {}
virtual void visit (Base*) const = 0;
virtual void visit (Derived<Type1>* d) const { visit(static_cast<Base*>(d)); };
//...
virtual void visit (Derived<TypeN>* d) const { visit(static_cast<Base*>(d)); };
};
struct FooVisitor : public Visitor
{
virtual void visit (Base* base) const override
{
std::cout<<"visiting base class."<<std::endl;
}
//further definitions for those types that require a special implementation
virtual void visit (Derived<TypeN>* d) const override
{
std::cout<<"visiting class of type Derived<TypeN>."<<std::endl;
}
};
它使您无法在基类中声明每个可能的变量类型,但可能效率低于之前的解决方案。
这种蛮力方法还有一些其他缺点,需要收集in chapter 11 of Alexandrescu's book。您还可以通过使用静态调度程序来阅读如何克服这些缺点。基本上你只需要输入你想要调度的类型,然后让代码创建上面的逻辑。