我正在尝试覆盖模板方法。 这是一个最小的例子:
#include <iostream>
class Base
{
public:
template <typename T>
void print(T var)
{
std::cout << "This is a generic place holder!\n";
}
};
class A: public Base
{
public:
template <typename T>
void print(T var)
{
std::cout << "This is A printing " << var << "\n";
}
};
class B: public Base
{
public:
template <typename T>
void print(T var)
{
std::cout << "This is B printing " << var << "\n";
}
};
int main()
{
Base * base[2];
base[1] = new A;
base[2] = new B;
base[1]->print(5);
base[2]->print(5);
delete base[1];
delete base[2];
return 0;
}
两种情况下的输出This is a generic place holder!
我怎样才能实现派生类的方法被调用?
如果该方法不是模板,我可以定义它virtual
,它可以按预期工作(已经尝试过),但它需要是一个模板。我做错了什么?
答案 0 :(得分:2)
调用Base::print
这两个调用的原因与print
是模板方法以及与非虚拟事实有关的事实无关。无论print
是什么,A::print
和B::print
都不会被考虑。
现在通过A::print
实际调用Base*
的典型解决方案是简单地使print
成为virtual
方法。但是,这是禁止的,因为print
是一个模板,您不能通过规则获得模板虚拟方法。
解决这个问题的一种方法是类型擦除。您可以使用Boost.TypeErasure执行以下操作:
typedef any<mpl::vector<
copy_constructible<>,
typeid_<>,
ostreamable<>
> > any_streamable;
struct Base {
virtual void print(any_streamable var) // not a template!
{
std::cout << "This is a generic place holder!\n";
}
};
struct A : Base {
void print(any_streamable var) {
std::cout << "This is A printing " << var << "\n";
}
};
对于像流媒体这样的简单事物,你也可以自己写作而不需要图书馆。
答案 1 :(得分:1)
首先,成员函数模板不能是虚拟的,派生类中的成员函数模板不能覆盖基类中的虚拟成员函数。
作为你的代码,指针的类型是Base *,所以在模板成员函数实例化中,Base中的函数被实例化,这就是函数为base的原因。
如果使用A *和B *,子实例中的函数将被实例化并调用。
答案 2 :(得分:1)
虽然非特定模板方法是虚拟的没有意义(例如,vtable会是什么样子?),但值得指出的是,模板方法的专业化没有理由不是虚拟的,允许它的重大好处。我将此作为N3405的最后一部分提交给C ++委员会。委员会尚未考虑过这个问题,但我甚至有更旧的论文需要考虑,所以仍有希望:)