当我们应用一个函数时,如果参数类型发生变化,那么原来的函数将不再适用,只有通过函数重载的方法重新实现一个适用于此参数类型的函数,如下所示。
int ADD (const int& _left , const int& _right)
{
return(_left+_right);
}
char ADD(const char& _left , const char& _right)
{
return(_left+_right);
}
int main()
{
cout<<ADD(1,2)<<endl;
cout<<ADD('A','1')<<endl;
}
运行结果如下:
但是通过函数重载实现所需函数有以下缺点:
- 只要有新类型出现就要重载一个新的函数。
- 这些函数除了类型外其余部分一样,代码复用率不高。
- 如果新的函数要求仅仅返回值不同,并没有满足函数重载的条件,函数重载不能提供这样的函数。
- 如果函数体出现错误,重载的函数也会出现错误,不好维护。
为了解决如上问题,引进泛型编程。泛型编程编写与类型无关的逻辑代码,是代码复用的一种手段,而其核心就是模板(包括函数模板和类模板,编译器可根据此模板合成所需函数或类)。
函数模板:代表了一个函数家族,该函数与类型无关,在使用时被参数化——产生所需的具体函数类型。模板函数具有以下格式:
template<class/typename T>
T 函数名(T 参数1 ,T 参数2 ,...)
{
函数功能
}
对于加法函数来说,其函数模板如下所示:
template<class T>
T ADD(T _left , T _right)
{
return(_left+_right);
}
下面我们通过函数模板来实现不同类型参数的加法运算。
template<class T>
T ADD(T& _left , T& _right)
{
return(_left+_right);
}
int main()
{
cout<<ADD(1,2)<<endl;
cout<<ADD('A','1')<<endl;
}
运行结果如下:
函数模板实例化
其实,在程序运行过程中,并不是直接通过函数模板得到运行结果,而是编译器将函数模板实例化——产生指定类型的函数,然后调用此函数得到结果。这个过程我们称为函数模板实例化,图解如下。
类型转换
函数模板的功能如此强大,那么编译器是否能通过函数模板实例化出任意类型的函数呢?答案是否定的。例如以下类型的函数,编译器并不能直接通过函数模板合成。
cout << ADD('A',1) << endl;
cout << ADD(1, 3.14) << endl;
原因是要求类型行参的实参必须完全匹配。
那么要通过函数模板实例化出上述类型的函数有什么方法呢?可以对函数实参进行类型转换(分为隐式和显式),如下所示。
//隐式类型转换
template<class T>
T ADD(const T& _left, const T& _right)
{
return(_left + _right);
}
int main()
{
cout << ADD((int)'A', 1) << endl;
cout << ADD((double)1, 3.14) << endl;
return 0;
}
//显式类型转换
template<class T>
T ADD(const T& _left, const T& _right)
{
return(_left + _right);
}
int main()
{
cout << ADD<int>('A', 1) << endl;
cout << ADD<double>(1, 3.14) << endl;
return 0;
}
运行结果如下:
注意:
- 当函数实参类型为const引用(指针)类型时,编译器可以根据参数为非const引用(指针)的函数模板实例化出。
- 编译器一般不会转换实参已匹配的实例化,会产生新的实例化。
如果模板形参不是引用类型,则对数组或函数类型的实参应使用常规指针转换。数组实参将当做指向其第一个元素的指针,函数实参当做指向函数类型的指针,如下图所示:当使用引用类型做函数模板时会报错,而使用非引用类型时不会报错。
非类型形参
函数模板的另一种参数——非类型形参。
若函数实参为数组或函数时,对应的函数模板应同时具有类型形参和非类型形参。
函数模板的重载
如下所示:
int MAX(const int&a,const int&b)
{
return a > b ? a : b;
}
template<class T>
T MAX(const T &_left, const T& _right)
{
return _left > _right ? _left : _right;
}
template<class T>
T MAX(const T& a,const T& b,const T& c)
{
return MAX(MAX(a, b), c);
}
int main()
{
MAX(10, 12);
MAX(10, 12, 30);
cout <<MAX(10,12) << endl;
cout <<MAX<>(10,12) << endl;
cout << MAX(10,12,30) << endl;
return 0;
}
运行结果如下:
注意:
- 如果一个普通函数和一个同名模板函数同时存在且其他条件都相同,则优先调用普通函数不会从该模板产生一个实例,但如果模板可以实例化出一个更加匹配的函数,那么将选择模板。
- 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。
- 显示指定一个空的模板实参列表,该语法告诉编译器只有模板才能匹配这个调用,而且该函数模板所有的模板参数都该根据实参演绎出来。