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); //调用模板的通用版本进行实例化注意:在模板特化版本的调用中,实参类型必须与特化版本函数的形参类型完全匹配,如果不匹配,编译器将为实参模板定义中实例化一个实例。