模板按其使用,可归结为两大类:函数模板和类模板。函数模板和普通函数相比,可谓异曲同工。普通函数也算得上是一种模板,只是施加于普通函数上的限制严格于函数模板而已。普通函数实现了基本的算法,调用函数时,需提供相同数目,相同类型的实参来代替形参。而模板函数除了要求参数的数目相同外,将形参的类型作了放大,因而其使用范围也随之变大。模板函数的实参类型只需要具有形参类型所必须的操作即可。
下面的例子定义了三个函数:
1//max.h
2#include <iostream>
3template <typename T>
4inline T const& max(T const& a,T const& b){
5 std::cout<<"你调用的是两个参数的模板函数!"<<std::endl;
6 return a>b?a:b;
7}
8
9template <typename T1,typename T2>
10inline T1 const& max(T1 const& a,T2 const&b){
11 std::cout<<"你调用的是两个参数的模板函数,且参数类型不同!"<<std::endl;
12 return a>b?a:b;
13}
14
15inline int const& max(int const& a,int const& b){
16 std::cout<<"你调用的是两个参数的普通函数!"<<std::endl;
17 return a>b?a:b;
18}
19
20template <typename T>
21inline T const& max(T const& a, T const& b ,T const& c){
22 std::cout<<"你调用的是三个参数的模板函数!"<<std::endl;
23 T d;
24 d=a>b?a:b;
25 return d>c?d:c;
26}
27
2#include <iostream>
3template <typename T>
4inline T const& max(T const& a,T const& b){
5 std::cout<<"你调用的是两个参数的模板函数!"<<std::endl;
6 return a>b?a:b;
7}
8
9template <typename T1,typename T2>
10inline T1 const& max(T1 const& a,T2 const&b){
11 std::cout<<"你调用的是两个参数的模板函数,且参数类型不同!"<<std::endl;
12 return a>b?a:b;
13}
14
15inline int const& max(int const& a,int const& b){
16 std::cout<<"你调用的是两个参数的普通函数!"<<std::endl;
17 return a>b?a:b;
18}
19
20template <typename T>
21inline T const& max(T const& a, T const& b ,T const& c){
22 std::cout<<"你调用的是三个参数的模板函数!"<<std::endl;
23 T d;
24 d=a>b?a:b;
25 return d>c?d:c;
26}
27
1#include <iostream>
2#include <string>
3#include "max.h"
4
5int main(){
6 int i=42;
7 std::cout<<"max(7,i) is "<<::max(7,i)<<std::endl;
8
9 double f1=3.45;
10 double f2=-9.34;
11 std::cout<<"max(f1,f2) is"<<::max(f1,f2)<<std::endl;
12
13 //下面的两行代码是正确的,前者明确指明参数的类型;后者通过类型转换都使得代码能正确运行。
14 std::cout<<"max(f1,i) is"<<::max<double>(f1,i)<<std::endl;
15 std::cout<<"max(f1,i) is"<<::max(f1,static_cast<double>(i))<<std::endl;
16
17 std::cout<<"max(f1,f2,f3)"<<::max(12.0,13.0,11.5)<<std::endl;
18 std::cout<<"max(char,int)"<<::max('a',12)<<std::endl;
19 std::cout<<"max(f1,i) is"<<::max(f1,i)<<std::endl;
20 std::cout<<"max(int,int)"<<::max<double>(12,34)<<std::endl;
21 std::cin>>i;
22}
2#include <string>
3#include "max.h"
4
5int main(){
6 int i=42;
7 std::cout<<"max(7,i) is "<<::max(7,i)<<std::endl;
8
9 double f1=3.45;
10 double f2=-9.34;
11 std::cout<<"max(f1,f2) is"<<::max(f1,f2)<<std::endl;
12
13 //下面的两行代码是正确的,前者明确指明参数的类型;后者通过类型转换都使得代码能正确运行。
14 std::cout<<"max(f1,i) is"<<::max<double>(f1,i)<<std::endl;
15 std::cout<<"max(f1,i) is"<<::max(f1,static_cast<double>(i))<<std::endl;
16
17 std::cout<<"max(f1,f2,f3)"<<::max(12.0,13.0,11.5)<<std::endl;
18 std::cout<<"max(char,int)"<<::max('a',12)<<std::endl;
19 std::cout<<"max(f1,i) is"<<::max(f1,i)<<std::endl;
20 std::cout<<"max(int,int)"<<::max<double>(12,34)<<std::endl;
21 std::cin>>i;
22}
上面的代码说明了模板函数的定义、模板函数的重载和使用等方面的问题,比较有意思的是模板函数的重载。和普通函数一样,模板函数也能重载,可以为模板函数定义参数数目不同的重载函数,也可以定义数目相同但参数类型不同的重载函数,而普通函数我们也可以当作是指定了具体类型的模板函数来处理。经过这样的处理,我们可以看出,模板函数的重载和普通函数的重载方式大同小异。
说完了模板函数的重载,我们再谈谈重载模板函数的使用,使用模板函数时,采用最符合原则进行调用,这也是为什么第14行的代码调用的是普通函数,而18,19行的代码调用的是两个参数类型不同的模板函数的原因。如果我们将第18,19行代码调用的模板函数去掉,那么代码仍然可以运行,其调用的将是普通函数,因为char和float都可以转换为int,最符合的函数原型是普通函数。
我们继续前面的话题。模板函数还是十分简单的,模板类则复杂的多。同模板函数一样,也能对模板类实施重载。当然,对类而言,使用重载似乎不是那么合适了,因而我们使用专门的词来称呼-专门化(Specialization)。而这种specialization,同模板函数重载一样,可以是完全specialization,也可是部分specialization(partial specialization),其实我们完全可以沿用前面处理函数的观点,将普通类(完全specialization的类)当作是一种特殊的模板类,那样无论是何种specialization,事实上都是模板类,只是类型参数的限制不同而已。如果进行这样的统一以后,对于重载模板类的定义、使用就同模板函数完全一致了,这里就不再多费口舌了。
模板类有一个模板函数不具有的特性-默认参数类型。模板类的类型参数设定时可以给出默认类型,使用的时候如果对于该参数不给出实参类型,将使用默认类型代替形参类型,这同函数中的默认参数值是一样的,不过模板函数是不允许这样做的。