C++是一种“强类型”语言。也就是说,对于一个变量,编译器必须确切知道它是什么类型。但是,这种强类型函数在实现一些简单函数反而更麻烦。例如:求两大数的较大者,应以Max( )函数,我们需要对不同数据类型分别定义不同重载版本来实现:
int Max(int x,int y) //比较两个int类型的值
{
return ((x > y) ? x : y);
}
float Max(float x,float y) //比较两个float类型的值
{
return ((x > y) ? x : y);
}
double Max(double x,double y) //比较两个double类型的值
{
return ((x > y) ? x : y);
}
我们看到,虽然我们可以通过函数重载去实现,但明显存在一些缺点:
1、所有的函数除返回类型外,函数体都相同,代码复用率低;
2、只要新类型出现,我们就需要添加新的对应函数;
3、维护不方便。
什么是模板?
C++程序有类和函数组成,模板分为类模板和函数模板。模板就是把功能相似,仅数据类型不同的函数或类设计为通用的函数模板或类模板,提供给用户。
模板是“泛型编程”的基础。简单的说,类是对象的抽象,而模板是类的抽象,用模板能定义具体类。
函数模板的一般定义形式:
template<typename Paraml,typename Paraml,... ,class Paraml>
返回类型 函数名(函数形参表)
{
}
模板定义以关键字template开始,形参由关键字class或typename及其后面的类型名构成,一般建议尽量使用typename。
注意:不能使用struct代替typename。
模板函数也可以定义为inline函数
template<typename T>
inline T Add(const T _left, const T _right)
{
return (_le _right);
}
注意:inline关键字必须放在模板形参表之后,返回值之前,不能放在template之前
例如:前面的Max()函数可以用模板定义如下:
template<typename T>
T Max(T x,T y)
{
return ((x > y) ? x : y);
}
模板是一个蓝图,它本身不是类也不是函数,编译器用模板产生的指定的类或者函数的特定类型版本,产生模板特定类型的过程称为函数模板实例化。
实例:定义一个函数模板,比较两个数大小。
#include<iostream>
using namespace std;
template<typename T>
T Max(T x, T y)
{
return ((x > y) ? x : y);
}
int main()
{
cout << Max(10, 20) << endl;
cout << Max(13.14, 5.2) << endl;
cout << Max(10, (int)5.2) << endl;
cout << Max<int>(10, 5.2) << endl;
}
模板参数 :1,类型参数
2,非类型参数
类型参数可以用来指定返回类型或函数的参数类型,以及在函数体内用于变量的声明或类型的转换:
template<typename T>
T foo(T* p)
{
T tmp = *p;
return tmp;
}
模板形参的名字在同一模板形参列表中只能使用一次。
非类型模板参数 :
一个非类型参数表示一个值而非一个类型。非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型参数。
例如数组长度:
说明:
1、一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例
化为这个非模板函数。
2、对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调动非模板
函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数,
那么将选择模板。
3、显式指定一个空的模板实参列表,该语法告诉编译器只有模板才能来匹配这个调用,
而且所有的模板参数都应该根据实参演绎出来。
4、模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。
模板函数特化
特化的一般形式:
1、关键字template后面接一对空的尖括号<>
2、函数名后接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参
3、函数形参表
4、函数体
例如,对于前面的Max()函数模板。
template<class T>
T Max(T x,T y)
{
return ((x>y) ? x:y);
}
如果比较的是两个“const char*”类型,那么函数模板用“const char*”型的模板实参实例化。如果还想让每个实参都被解释为C风格的字符串而不是字符指针,那么饿通过模板定义给出的语义就不正确了,必须为函数模板实例化提供“const char*”的特化:
#include<cstring> //引入cstring的相关声明
template<> //特化标志
const char* Max<const char*>(const char* x,const char* y) //用const char*转化
{
return (strcmp(x,y)<0)?x:y;
}
特化的声明必须与特定的模板相匹配。
由于有了这个特化,程序中对所有用两个“const char*”型形参进行调用的Max()都会调用这个特化的定义,而对于其他的调用,则通过模板定义实例化对象:
const char* a = “hello”;
const char* b = "world";
int x = 10;
int y = 20;
Max(a,b); //调用模板的特化版本进行实例化
Max(x,y); //调用模板的通用版本进行实例化
注意:在模板特化版本的调用中,实参类型必须与特化版本函数的形参类型完全匹配,如果不匹配,编译器将为实参模板定义中实例化一个实例。