我有两个功能
MultiplyVerison1(T x, T y); // in class A
MultiplyVersion1(T x, T y); // in class B
以上函数位于单独的非模板类中。
现在,作为重构的一部分,我尝试创建 A
和 B
的基类,并创建一个 纯虚拟 MultiplyVersion1
但是模板函数不能被标记为虚拟。
那么,我们怎样才能用模板函数实现同样的效果呢?
最佳答案
你不能。无法通过基类指针调用派生类中的函数模板,这就是“函数模板不能是虚拟的”的意思。
您可以认为这是因为它是触发具有特定类型 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
公开继承,而是私有(private)继承并引入您想要通过 using
语句重用的函数)。如果您想定义重复使用的函数,请调用 MultiplyVersion1
,然后考虑通过 CRTP 模拟动态绑定(bind):
#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);
}
关于c++ - 如何导出模板函数?或者这种情况的首选方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9033189/