函数模板并非真正的函数。
template<class 类型形参1,class 类型形参2,....>
返回值类型 函数模板名(调用形参1,调用形参2,....){
......
}
每个函数模板都必须加上关键字template
;每个类型形参前都要加关键字class
或typename
;除去template
这一行,剩下实为函数定义形式;
例如:
template<class T> T Max(T x, T y){
return x > y ? x : y;
}
使用函数模板必须对函数模板进行实例化
形式 : 函数模板名<类型实参1,类型实参2,...>(调用实参1,....);
例如 :
Max<int>(123,456);
Max<double>(12.3,45.6);
Max<string>("hello","world");
1.1中类型形参为T,此处类型实参分别为int、double、string。
编译器并不是把函数模板编译成一个可以处理任何数据类型的单一实体。
编译器在实例化函数模板时根据类型实参从函数模板中产生一个真正的函数实体。
函数模板并不是一个函数实体,通过实例化才能产生真正的函数实体。
函数模板可以看成是编译器生成函数实体的一个依据而已。
这种用具体数据类型替换函数模板类型形参的过程叫做实例化,这个过程将产生一个函数模板的实例(函数实体)。
只要使用函数模板,就会自动引发编译器的实例化过程,因此程序员不需要额外地请求对函数模板的实例化
原则上来说可以使用任何类型来实例化函数模板,不管其为基本类型还是类类型。
但前提是这个类型必须支持函数模板所要执行的操作。
例如:一个不支持 “>” 操作符的类型如下代码中自定义的Integer来实例化Max函数模板,编译器将报错误;此时若想解决此问题,则应在Integer中重载 “>” 操作符。
bool operator>(Integer const& that)const{
return m_i > that.m_i;
}
整体代码:
funTemplate1.cpp
#include
using namespace std;
class Integer{
public:
Integer(int i):m_i(i){}
bool operator>(Integer const& that)const{
return m_i > that.m_i;
}
private:
int m_i;
};
//函数模板
//此处分为两行是便于观看,一行也没问题
template<class T>
T Max(T x, T y){ return x >y ? x : y}
//编译器工作
//int Max(int x, int y){....}
//double Max(double x, double y){...}
//string Max(string x, string y){...}
//Integer Max(Integer x, Integer y){ return x > y ? x : y; }
int main(){
Integer ix=100, iy=200;
Max<Integer>(ix,iy);
int nx=10, ny=20;
cout << Max<int>(nx,ny) << endl;
// 编译器 Max(nx,ny)
double dx=12.3, dy=45.6;
cout << Max<double>(dx,dy) << endl;
// 编译器 Max(dx,dy)
string sx="world", sy="hello";
cout << Max<string>(sx,sy) << endl;
// 编译器 Max(sx,sy)
return 0;
}
编译器对函数模板都会进行两次编译。
第一次编译发生在实例化函数模板之前(产生真正函数实体之前)只检查函数模板本身内部代码,查看基本词法是否正确。
1)函数模板内部出现的所有标识符是否均有声明
2)对于已知类型的调用要查看调用是否有效
3)对于未知类型调用认为都合理(符号<>j尖括号除外,若出现<>尖括号必报错)
第二次编译发生在实例化函数模板之后(产生真正函数实体之后)
此时不再有未知类型,结合所使用的类型实参,再次检查模板代码,查看所有调用是否真的均有效。
twoCompile.cpp
#include
using namespace std;
class A{
public:
void func(){
cout << "A::func()" << endl;
}
};
template<class T>void foo(){
// fdjslfj; //若不写下方代码,编译时将会报错fdjslfj,说明编译器有检车函数模板内部标识符是否有声明。
A a;
//a.ajfdifunc(); //报错,已知类型无效调用
a.func();//已知类型调用
T t;
t.func();//未知类型调用
}
int main(){
foo<A>();
return 0;
}
template<class T>T Max(T x,T y){…}
class T和Max(T x,T y)中均为T,相同,即为相关。
例如:
Max(123,456); Max<int>(123,456);
获得和调用普通函数一致的语法表现形式。可不加<>,直接使用Max(nx,ny)。
三种情况不能做隐式推断
1) 调用参数 和 类型参数 不完全相关
例如:
template<class T,class D>T Max(T x,T y){}
此时可在调用的时候增加调用参数的类型如:
template<class T,class D>void Func(D x){
}
Func<string>(nx);
2) 隐式推断不支持隐式类型转换
隐式推断和隐式类型转换不可同时存在。
例如:
template<class T> T Max(T x, T y){…}
使用时 :
Max(123,45.6);
3) 返回值类型不支持隐式推断。
deduction.cpp
#include
using namespace std;
//函数模板
template<class T>T Max(T x, T y){
return x > y ? x : y;
}
template<class T,class D>void Func(D x){
}
template<class R,class T>R Hum(T x){
R r;
return r;
}
int main(){
int nx=10, ny=20;
cout << Max(nx,ny) << endl;
// Max<>(nx,ny)==>Max(nx,ny)
double dx=12.3, dy=45.6;
cout << Max(dx,dy) << endl;
// Max(dx,dy)
string sx="world", sy="hello";
cout << Max(sx,sy) << endl;
// Max(sx,sy)
Func<string>(nx);
Max(nx,(int)dy);
Hum<double>(nx);
return 0;
}
普通函数和可实例化出该函数的函数模板构成重载关系。
在数据类型匹配度相同情况下编译器优先选择普通函数。
除非函数模板可以产生具有更好的数据类型匹配度的实例。
函数模板的实例化不支持隐式类型转换但普通函数支持。
在传递参数时如果需要编译器做隐式类型转换,则编译器选择普通函数。
可以在实例化时用<>强行通知编译器选择函数模板。
overload.cpp
#include
using namespace std;
void Max(int x, int y){
cout << "1:Max(int,int)" << endl;
}
template<class T>void Max(T x, T y){
cout << "2:Max(T,T)" <<endl;
}
int main(){
int nx=10, ny=20;
Max(nx,ny);
Max<>(nx,ny);
double dx=12.3, dy=45.6;
Max(dx,dy); // 除非函数模板可以产生具有更好的数据类型匹配度的实例
Max(nx,dy); //在传递参数时如果需要编译器做隐式类型转换,则编译器选择普通函数。
return 0;
}