我听说c++的类成员函数模板不能是虚的。这是真的吗?
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
我听说c++的类成员函数模板不能是虚的。这是真的吗?
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
当前回答
从c++模板的完整指南:
Member function templates cannot be declared virtual. This constraint is imposed because the usual implementation of the virtual function call mechanism uses a fixed-size table with one entry per virtual function. However, the number of instantiations of a member function template is not fixed until the entire program has been translated. Hence, supporting virtual member function templates would require support for a whole new kind of mechanism in C++ compilers and linkers. In contrast, the ordinary members of class templates can be virtual because their number is fixed when a class is instantiated
其他回答
如果预先知道模板方法的类型集,则'虚拟模板方法'有一个变通方法。
为了说明这个想法,在下面的例子中只使用了两种类型(int和double)。
在那里,一个“虚拟”模板方法(Base:: method)调用相应的虚拟方法(Base:: VMethod之一),后者反过来调用模板方法实现(Impl::TMethod)。
只需要在派生实现(AImpl, BImpl)中实现模板方法TMethod,并使用derived <*Impl>。
class Base
{
public:
virtual ~Base()
{
}
template <typename T>
T Method(T t)
{
return VMethod(t);
}
private:
virtual int VMethod(int t) = 0;
virtual double VMethod(double t) = 0;
};
template <class Impl>
class Derived : public Impl
{
public:
template <class... TArgs>
Derived(TArgs&&... args)
: Impl(std::forward<TArgs>(args)...)
{
}
private:
int VMethod(int t) final
{
return Impl::TMethod(t);
}
double VMethod(double t) final
{
return Impl::TMethod(t);
}
};
class AImpl : public Base
{
protected:
AImpl(int p)
: i(p)
{
}
template <typename T>
T TMethod(T t)
{
return t - i;
}
private:
int i;
};
using A = Derived<AImpl>;
class BImpl : public Base
{
protected:
BImpl(int p)
: i(p)
{
}
template <typename T>
T TMethod(T t)
{
return t + i;
}
private:
int i;
};
using B = Derived<BImpl>;
int main(int argc, const char* argv[])
{
A a(1);
B b(1);
Base* base = nullptr;
base = &a;
std::cout << base->Method(1) << std::endl;
std::cout << base->Method(2.0) << std::endl;
base = &b;
std::cout << base->Method(1) << std::endl;
std::cout << base->Method(2.0) << std::endl;
}
输出:
0
1
2
3
注: Base::Method对于实际代码来说实际上是多余的(VMethod可以被设为public并直接使用)。 我添加它,使它看起来像一个实际的“虚拟”模板方法。
不,他们不能。但是:
template<typename T>
class Foo {
public:
template<typename P>
void f(const P& p) {
((T*)this)->f<P>(p);
}
};
class Bar : public Foo<Bar> {
public:
template<typename P>
void f(const P& p) {
std::cout << p << std::endl;
}
};
int main() {
Bar bar;
Bar *pbar = &bar;
pbar -> f(1);
Foo<Bar> *pfoo = &bar;
pfoo -> f(1);
};
如果您想要做的只是拥有一个公共接口并将实现推迟到子类,则效果大致相同。
我看了所有的14个答案,有些有原因,为什么虚拟模板的功能不能工作,其他人显示了一个工作周围。一个答案甚至表明虚类可以有虚函数。这不足为奇。
我的回答将给出一个直接的理由,为什么标准不允许虚模板函数。因为很多人都在抱怨。首先,我不敢相信有人说虚函数可以在编译时推导出来。这是我听过的最蠢的话。
不管怎样。我确定标准规定指向对象的this指针是其成员函数的第一个参数。
struct MyClass
{
void myFunction();
}
// translate to
void myFunction(MyClass*);
既然我们都清楚了。然后,我们需要知道模板的转换规则。模板化的参数在它可以隐式转换的内容上受到极大的限制。我不记得所有的内容,但是你可以查看c++ Primer以获得完整的参考。例如,T*可转换为const T*。数组可以转换为指针。但是,派生类不能作为模板形参转换为基类。
struct A {};
struct B : A {};
template<class T>
void myFunction(T&);
template<>
void myFunction<A>(A&) {}
int main()
{
A a;
B b;
myFunction(a); //compiles perfectly
myFunction((A&)b); // compiles nicely
myFunction(b); //compiler error, use of undefined template function
}
我希望你们能明白我的意思。你不能使用虚拟模板函数,因为就编译器而言,它们是两个完全不同的函数;作为隐式参数,此参数具有不同的类型。
虚拟模板不能工作的另一个原因同样有效。因为虚表是快速实现虚函数的最佳方式。
不可以,模板成员函数不能为虚函数。
虽然很多人已经回答了一个老问题,但我相信一个简洁的方法,与其他发布的方法没有太大不同,就是使用一个小宏来帮助减轻类声明的重复。
// abstract.h
// Simply define the types that each concrete class will use
#define IMPL_RENDER() \
void render(int a, char *b) override { render_internal<char>(a, b); } \
void render(int a, short *b) override { render_internal<short>(a, b); } \
// ...
class Renderable
{
public:
// Then, once for each on the abstract
virtual void render(int a, char *a) = 0;
virtual void render(int a, short *b) = 0;
// ...
};
现在,要实现我们的子类:
class Box : public Renderable
{
public:
IMPL_RENDER() // Builds the functions we want
private:
template<typename T>
void render_internal(int a, T *b); // One spot for our logic
};
这样做的好处是,当添加一个新支持的类型时,它可以从抽象头文件中完成,而不必在多个源文件/头文件中进行修改。