虚拟模板函数访问者变通方法与模板派生类

时间:2014-11-24 03:55:48

标签: c++ templates virtual

问题是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中的一些辅助函数。

2 个答案:

答案 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*

DEMO


编辑:这是使用基本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。您还可以通过使用静态调度程序来阅读如何克服这些缺点。基本上你只需要输入你想要调度的类型,然后让代码创建上面的逻辑。

DEMO