如何派生模板函数?或者这种情况的首选方法是什么?

时间:2012-01-27 12:22:17

标签: c++ templates virtual

我有两个功能

MultiplyVerison1(T x, T y); // in class A
MultiplyVersion1(T x, T y); // in class B

以上函数位于单独的非模板类中。

现在,作为重构的一部分,我正在尝试创建AB的基类并创建pure virtual MultiplyVersion1,但是 templte函数不能标记为虚拟。

那么,我们如何通过模板函数实现相同的目标呢?

1 个答案:

答案 0 :(得分:3)

你做不到。没有办法通过指向基类的指针调用派生类中的函数模板,这就是“函数模板不能虚拟”的含义。

你可以认为这是因为它是用特定类型T触发函数模板实例化的调用 - 如果用int调用它,但动态类型是你调用它的对象直到运行时才知道(无论是A还是B还是别的),编译器无法知道它需要实例化A::MultiplyVersion1<int>B::MultiplyVersion1<int>或者别的。实际上还有更多的东西,但我认为这已经足够了。

你可以躲避特定情况,但你不会得到虚拟功能的全部效果。类似的东西:

struct Base {
    template <typename T>
    void MultiplyVersion1(const T &x, const T &y) {
        A *athis = dynamic_cast<A*>(this);
        if (athis) {
            athis->MultiplyVersion1(x,y);
        } else {
            B *bthis = dynamic_cast<B*>(this);
            if (bthis) {
                bthis->MultiplyVersion1(x,y);
            } else {
                throw std::logic_error();
            }
        }
    }
    virtual ~Base() {}
};

现在,当您通过指向基地的指针调用MultiplyVersion1<int>时,A::MultiplyVersion1<int>B::MutiplyVersion1<int>都会被实例化。但当然你不能轻易添加新的派生类,这是一个严重的限制。

您还可以重新考虑是否真的需要动态多态,但这完全取决于您计划如何使用该基类。到目前为止,没有它你似乎已经做好了。

如果你想从基类中获得所有其他函数的代码重用,那么你就不需要动态多态。将MultiplyVersion1完全从基类中删除(也许不要从Base公开继承,而是私下继承并引入要与using语句重用的函数) 。如果您要为重新使用而定义的函数调用MultiplyVersion1,那么请考虑通过CRTP模拟动态绑定:

#include <iostream>

template <typename Derived>
struct Base {
    template <typename T>
    void MultiplyVersion2(const T &x, const T &y) {
        static_cast<Derived&>(*this).MultiplyVersion1(x + 1, y + 1);
    }
};

struct A : private Base<A> {
    friend class Base;
    template <typename T> void MultiplyVersion1(T x, T y) {
        std::cout << x*y << "\n"; 
    }
    using Base::MultiplyVersion2;
};

struct B : private Base<B> {
    friend class Base;
    template <typename T> void MultiplyVersion1(T x, T y) {
        std::cout << x << " * " << y << " = " << x*y << "\n"; 
    }
    using Base::MultiplyVersion2;
};

int main() {
    A a;
    a.MultiplyVersion2(1,2);
    B b;
    b.MultiplyVersion2(1,2);
}