Virtual template function visitor workaround with template derived classes
问题是,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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的值大约为100,并且不断引入新的D类。每个都将以同样的方式使用d。为了简单起见,假设每个访问函数将使用d
1 | func<D>(); |
在哪里?
1 | template <typename D> void Base::func(); |
是基础中的某个助手函数。
这里有一个可行的解决方案。请注意,这里的假设是您只使用正确的类型进行调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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); } |
Jarod42提到了一个可能的解决方案,即指定所有可能发生的类型。然而,通常,您也希望给出一个采用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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; } }; |
演示
编辑:这里还有一种使用基本双调度的可能性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | struct Visitor { virtual ~Visitor() {} virtual void visit (Base*) const = 0; }; struct FooVisitor : public Visitor { virtual void visit (Base* base) const override { if(Derived<TypeN>* d = dynamic_cast<Derived<TypeN>*>(base)) { std::cout<<"visiting class of type Derived<TypeN>."<<std::endl; } else { std::cout<<"visiting base class."<<std::endl; } } }; |
它使您不必在基类中声明每个可能的变量类型,但可能比以前的解决方案效率低。
这种蛮力的方法还有一些其他的缺点,这在亚历山德里斯科的书的第11章中有所收集。您还可以在这里阅读如何使用静态分派器来克服这些缺点。基本上,只需输入一次要考虑分派的类型,然后让代码创建上述逻辑。
演示