- 模板说明
为了定义函数模板,首先要进行模板说明,其作用是说明模板中使用的类属参数。模板说明有形式有两种:
template <class T1, class T2, ... class Tn>
//或者是:
template<typename T1, typename T2, ... typename Tn>
为了增强程序的可读性,建议使用关键字typename
。关键字template
表示正在说明一个模板,而T1,T2,...Tn
是等待实例化的类属参数。它们所对应的实际参数可以是int、double、char
等基本类型,也可以是指针、类等各种用户已经定义的类型。
函数模板
- 假设我们要写一个函数,比较两个整数的最大值,正确代码如下:
int Max(int a, int b)
{
return a > b? a : b;
}
如果我们还要比较两个double
类型,float
等类型的最大值,那我们就需要继续写参数和返回值不同,而实现功能相同的函数。这样的话会显得很繁琐。浪费时间~~
- 那我们可不可以只用一个函数来实现不同数值类型之间的最大值比较?答案是可以的。我们可以用模板来实现。具体代码如下:
template<typename T>
T Max(const T a, const T b)
{
return a > b? a : b;
}
在上面代码中,当程序调用Max
函数时,编译器会根据传递给函数Max
的参数来自动判断形式参数a
和b
的数值类型,
我们可以用下面函数调用Max
函数证明:
int main()
{
int x1 = 5, x2 = 6;
cout<<"第一次调用后的结果为:"<<Max(x1, x2)<<endl;
double x3 = 4.8, x4 = 5.9;
cout<<"第二次调用后的结果为:"<<Max(x3, x4)<<endl;
}
运行结果为:
第一次调用后的结果为:6
第二次调用后的结果为:5.9
- 我们可以看到,两次调用
Max
函数,传递的参数类型都不相同,但是程序依然可以正常运行。这是因为两次调用都分别给函数进行不同的实例化。下图给出了函数模板和模板函数的关系示意图。这些重载函数,通过函数模板按实际类型生成,所以称为模板函数,而这个过程称为实例化。
重载函数模板
1. 函数模板重载
- 重载函数模板便于定义类属参数,或者由于函数参数的类型、个数不同所进行的类似操作,比如上面
Max
函数是返回两个数的最大值,我们可以将Max
函数模板重载为求数组最大元素的函数模板。具体代码如下:
template<typename T>
T Max(T *a, int n)
{
T temp = a[0];
for(int i = 1; i < n; i++)
{
temp = temp > a[i]? temp : a[i];
}
}
2. 用普通函数重载函数模板
- 函数模板实例化的时候,实际参数类型替换类属参数。具有类型检查功能,却没有普通传值参数类型转换机制。例如:当调用第一个
Max
函数比较两个值大小时,两个实际参数必须类型相同。若用以下调用,则编译器会报错
int main()
{
int k = 3; double c = 2.9;
Max(k, c);
}
- 当用以下函数重载
Max
函数时,再用上面调用方式,则不会发生报错
int Max(int a, double b)
return a > b? a : b;
重载函数int Max(int a, double b)
可以隐式进行数据类型转换。
- 如果重载函数改为:
int Max(int a, int b)
return a > b? a : b;
则不同的编译器可能导致不同的结果(一般都会报错)。
编译器通过匹配过程确定调用哪个哪个函数。匹配顺序如下:
- 寻找和使用最符合函数名和参数类型的函数,若找到,则使用他;
- 否则,寻找一个函数模板,将其实例化,产生一个匹配的模板函数,若找到,则调用它;
- 否则,寻找可以通过类型转换进行参数匹配的重载函数,若找到,则使用它。
如果按以上步骤均未能找到匹配函数,则这个调用是错误的;如果这个调用有多于一个的匹配选择,则调用匹配产生二义性,也是错误的。