我有两个功能
MultiplyVerison1(T x, T y); // in class A
MultiplyVersion1(T x, T y); // in class B
以上函数位于单独的非模板类中。
现在,作为重构的一部分,我正在尝试创建A
和B
的基类并创建pure virtual MultiplyVersion1
,但是 templte函数不能标记为虚拟。
那么,我们如何通过模板函数实现相同的目标呢?
答案 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);
}