泛型编程编写与类型无关的通用代码是代码复用的一种手段。模板是泛型编程的基础。二者区别模板是实现泛型编程的核心工具 泛型编程是一种编程思想先看看下面这些代码voidSwap(intleft,intright){inttempleft;leftright;righttemp;}voidSwap(doubleleft,doubleright){doubletempleft;leftright;righttemp;}voidSwap(charleft,charright){chartempleft;leftright;righttemp;}每次使用不同类型的交换函数函数都要重新写函数代码复用率很低使用函数模板就能解决模板分类函数模板概念函数模板代表了一个函数家族该函数模板与类型无关在使用时被参数化根据实参类型产生函数的特定类型版本。函数模板表达式 templatetypename T1, tyeoname T2, ...... ,typename Tn 返回值类型 函数名参数列表 { 函数体 }例如前面的Swap函数templatetypenameTvoidSwap(Tleft,Tright){T tmpleft;leftright;righttmp;}注意typename是用来定义模板参数关键字也可以使用class(切记不能使用struct代替class)例如#includeiostreamusingnamespacestd;//templatetypename TtemplateclassTvoidSwap(Tleft,Tright){T tmpleft;leftright;righttmp;}intmain(){intx11;inty22;doublea1.1,b2.2;coutx yendl;Swap(x,y);coutx yendl;couta bendl;Swap(a,b);couta bendl;return0;}这个T可以换成其他的但一般用T因为他是Type的缩写;函数模板原理在使用时它俩调用的是同一个函数吗 不是这就要说到函数模板原理在编译器编译阶段对于模板函数的使用编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如当用double类型使用函数模板时编译器通过对实参类型的推演将T确定为double类型然后产生一份专门处理double类型的代码对于字符类型也是如此验证如下通过F10调试看两个调用函数的反汇编是单独的两个因为交换函数在C中经常使用所以小写的swap是直接是存放在类模板库中的我们可以直接调用函数模板的实例化用不同类型的参数使用函数模板时称为函数模板的实例化。模板参数实例化分为隐式实例化和显式实例化。这里以Add函数为例templateclassTTAdd(constT left,constT right)//注意加上const支持常量对象和右值的传入避免潜在的使用限制。{returnleftright;}intmain(){inta20,b24;doublec2.,d2.4;//隐式实例化 --- 让编译器根据实参推演模板参数的实际类型 --- 需要编译器去推导//模板在推演的过程中,他只会推演已经使用的类型它不会说是比如这里调用的是int和double它不说是会给你推演出一个char类型的coutAdd(a,b)endl;coutAdd(c,d)endl;//cout Add(a, d); endl;//err -- 将a推演出为int, 将d推演出为double但只有一个T编译器无法确定T为int还是double而报错//解决办法//1. 用户自己来强制转化coutAdd(a,(int)d)endl;//// 2. 使用显式实例化 -- 在函数名后的中指定模板参数的实际类型 -- 不用编译器去推导而直接显示类型的coutAddint(a,d)endl;return0;}显示实例化特例调用函数是时也可以用到templateclassTT*Func(size_t n){returnnewT[n];}intmain(){Funcint(10);Funcdouble(10);return0;}如果类型不匹配编译器会尝试进行隐式类型转换如果无法转换成功编译器将会报错。总结使用函数模板就是将编译器当牛马实例化生成对应的函数模板参数的匹配原则1.有现成的吃现成的实在是想要调用模板再函数名前面加显示实例化调用例如intAdd(intleft,intright){returnleftright;}templatetypenameTTAdd(T left,T right){return(leftright)*10;}intmain(){inta22,b12;coutAdd(a,b)endl;//若想要调用模板Add那就使用显示实例化coutAddint(a,b)endl;return0;}运行结果如下通过结果来看第一个调用的是函数第二个调用的是函数模板里的2.C 允许具体的非模板函数和同名函数模板共存优先调用能精确匹配的非模板函数“有现成的吃现成的”如果想要强制使用模板可通过显式实例化函数名后加实现倘若调用时参数类型无法与非模板函数精确匹配包括非模板需隐式转换才能匹配、或完全不匹配且模板能直接匹配参数类型则会选择模板实例化调用。例如intAdd(intleft,intright){returnleftright;}templateclassT1,classT2T1Add(T1 left,T2 right){return(leftright)*10;}intmain(){inta10;doubleb2.5;coutAdd(a,b)endl;return0;}运行结果如下通过结果发现它优先调用的是函数模板3. 当模板的参数相同和不同的情况下的匹配原则:匹配与之最相近的例如templatetypenameTTAdd(T left,T right){return(leftright)*10;}templateclassT1,classT2T1Add(T1 left,T2 right){return(leftright)*10;}intmain(){inta10;doubleb2.5;intc25;coutAdd(a,b)endl;//调用函数模板参数不同的那个coutAdd(a,c)endl;//调用函数模板参数相同的那个return0;}运行结果如下C允许具体的函数和同名的函数模板同时存在的原因它在函数名上做了手脚验证前面的代码用F10调试起来看反汇编具体代码intAdd(intleft,intright){returnleftright;}templateclassT1,classT2T1Add(T1 left,T2 right){return(leftright)*10;}intmain(){coutAdd(1,2)endl;coutAdd(1,2.2)endl;return0;}这是具体的函数的函数名这是函数模板的函数名模板参数和函数参数定义方法很相似例如templateclassT1,classT2//类型T1Add(T1 left,T2 right)//对象//这里记得加上{return(leftright)*10;}类模板类模板的定义格式templateclass T1, class T2 .... class Tn class 类模板名 { 类内成员函数 };用类模板实现栈//类模板templateclassTclassStack{public:Stack(size_t capacity4){_arraynewT[capacity];_capacitycapacity;_size0;}voidPush(constTdata);//const不要拼错private:T*_array;size_t _capacity;size_t _size;};// 声明和定义分离的写法// 模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误templateclassTvoidStackT::Push(constTdata){//扩容_array[_size]data;_size;};intmain(){// 实例化生成对应的类这里是三个不同的类型Stackintst1;Stackdoublest2;Stackcharst2;return0;}类模板实例化与函数模板实例化不同类模板实例化需要在类模板名字后跟然后将实例化的类型放在中即可类模板名字不是真正的类而实例化的结果才是真正的类。// Stack是类名Stackint才是类型 Stackint st1; // int Stackdouble st2; // double