我听说c++的类成员函数模板不能是虚的。这是真的吗?
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
我听说c++的类成员函数模板不能是虚的。这是真的吗?
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
当前回答
我看了所有的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
}
我希望你们能明白我的意思。你不能使用虚拟模板函数,因为就编译器而言,它们是两个完全不同的函数;作为隐式参数,此参数具有不同的类型。
虚拟模板不能工作的另一个原因同样有效。因为虚表是快速实现虚函数的最佳方式。
其他回答
不可以,模板成员函数不能为虚函数。
我看了所有的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
}
我希望你们能明白我的意思。你不能使用虚拟模板函数,因为就编译器而言,它们是两个完全不同的函数;作为隐式参数,此参数具有不同的类型。
虚拟模板不能工作的另一个原因同样有效。因为虚表是快速实现虚函数的最佳方式。
下面的代码可以在windows 7上使用mingwg++ 3.4.5编译并正常运行:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class A{
public:
virtual void func1(const T& p)
{
cout<<"A:"<<p<<endl;
}
};
template <typename T>
class B
: public A<T>
{
public:
virtual void func1(const T& p)
{
cout<<"A<--B:"<<p<<endl;
}
};
int main(int argc, char** argv)
{
A<string> a;
B<int> b;
B<string> c;
A<string>* p = &a;
p->func1("A<string> a");
p = dynamic_cast<A<string>*>(&c);
p->func1("B<string> c");
B<int>* q = &b;
q->func1(3);
}
输出为:
A:A<string> a
A<--B:B<string> c
A<--B:3
后来我又添加了一个新类X:
class X
{
public:
template <typename T>
virtual void func2(const T& p)
{
cout<<"C:"<<p<<endl;
}
};
当我试图在main()中像这样使用类X时:
X x;
x.func2<string>("X x");
g++报告以下错误:
vtempl.cpp:34: error: invalid use of `virtual' in template declaration of `virtu
al void X::func2(const T&)'
所以很明显:
虚成员函数可以在类模板中使用。编译器可以很容易地构造虚表 将类模板成员函数定义为虚函数是不可能的,如你所见,很难确定函数签名和分配虚表项。
虽然很多人已经回答了一个老问题,但我相信一个简洁的方法,与其他发布的方法没有太大不同,就是使用一个小宏来帮助减轻类声明的重复。
// 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
};
这样做的好处是,当添加一个新支持的类型时,它可以从抽象头文件中完成,而不必在多个源文件/头文件中进行修改。
在其他答案中,建议的模板函数是一个门面,并不能提供任何实际的好处。
模板函数对于只编写一次代码很有用 不同的类型。 虚函数对于为不同的类提供公共接口非常有用。
该语言不允许虚拟模板函数,但通过一个变通方法,可以同时拥有两者,例如,每个类都有一个模板实现和一个虚拟公共接口。
但是,有必要为每个模板类型组合定义一个虚拟包装器函数:
#include <memory>
#include <iostream>
#include <iomanip>
//---------------------------------------------
// Abstract class with virtual functions
class Geometry {
public:
virtual void getArea(float &area) = 0;
virtual void getArea(long double &area) = 0;
};
//---------------------------------------------
// Square
class Square : public Geometry {
public:
float size {1};
// virtual wrapper functions call template function for square
virtual void getArea(float &area) { getAreaT(area); }
virtual void getArea(long double &area) { getAreaT(area); }
private:
// Template function for squares
template <typename T>
void getAreaT(T &area) {
area = static_cast<T>(size * size);
}
};
//---------------------------------------------
// Circle
class Circle : public Geometry {
public:
float radius {1};
// virtual wrapper functions call template function for circle
virtual void getArea(float &area) { getAreaT(area); }
virtual void getArea(long double &area) { getAreaT(area); }
private:
// Template function for Circles
template <typename T>
void getAreaT(T &area) {
area = static_cast<T>(radius * radius * 3.1415926535897932385L);
}
};
//---------------------------------------------
// Main
int main()
{
// get area of square using template based function T=float
std::unique_ptr<Geometry> geometry = std::make_unique<Square>();
float areaSquare;
geometry->getArea(areaSquare);
// get area of circle using template based function T=long double
geometry = std::make_unique<Circle>();
long double areaCircle;
geometry->getArea(areaCircle);
std::cout << std::setprecision(20) << "Square area is " << areaSquare << ", Circle area is " << areaCircle << std::endl;
return 0;
}
输出:
方形面积为1,圆形面积为3.1415926535897932385
在这里试试