一、函数模板介绍
函数模板是一个生成函数的公式,可以用来生成针对特定类型的函数版本;
如:求两个具有相同的数据类型的数值中的最大值;普通的一个函数只能使用于某种特定类型,不能适用于其他数据类型,而使用函数模板则可以进行泛型编程,
对于使用该函数模板的各个数据类型在编译时期则会生成相应的适用于该数据类型的函数版本,这样就避免对于每一种数据类型编写相应的函数版本,
可以加快开发速度,减少不必要的重复代码。
template<typename T> T Max(const T &lhs, const T &rhs) { return lhs > rhs ? lhs : rhs; }
二、函数模板定义
1、函数模板定义方式: 以关键字template开始,后接由一个或者多个模板参数组成的模板参数列表,通常模板参数可以是模板类型参数或者非类型模板参数;
2、模板参数的作用:模板参数表示在类或者函数作用域中需要用到的类型或者值,在使用模板时,通过显式或者隐式的指定模板实参,可以将其绑定到模板参数上
三、函数模板的使用与实例化
a = 0.0, b = 1.0; std::cout << Max(a, b) << endl;
在使用函数模板时,编译器在编译时期会根据使用函数模板的对象来推断出函数模板实参,并由推断出来的函数模板实参实例化一个特定版本的函数;
编译器实例化一个特定版本的函数时,会用推断出的函数模板实参替代函数模板中的模板参数,这些编译器生成的特定版本的函数通常被称为该模板的实例;
在上面的例子中,会生成如下的特定版本的函数;
double Max(const double &lhs, const double &rhs) { return lhs > rhs ? lhs : rhs; }
四、函数模板实参推断
1、模板实参推断:在函数模板被使用时,编译器会根据模板函数的实参来推断出函数模板的实参,这个过程称为模板实参推断
2、模板实参与形参的类型转换:如果一个函数形参类型使用了函数模板实参,那么将根据下面的规则对实参进行类型转换,再进行模板实参推断
(1)const转换:可以将一个非const对象的引用或者指针传递给一个const引用或者指针
(2)数组或者函数指针转换:如果函数形参为非引用类型,则可以对一个数组或者函数类型的实参应用正常的指针转换;
五、函数模板显式实参
在函数模板中,编译器可以根据函数模板的实参来推断出函数模板的实参,但当函数的返回值的类型与函数的所有形参的类型不同时,编译器根据调用该
模板函数的实参推断出函数返回值的类型,这时需要显式的指定实参;如定义一个求最大值的函数,其接受两个相同类型的函数实参,如int、unsigned类型,
但是需要返回float或者double类型;
template<typename T, typename U> T Max(const U &lhs, const U &rhs) { return lhs > rhs ? lhs : rhs; } int main() { unsigned u_a = 0, u_b = 1; int int_a = 0, int_b = -1; std::cout << Max<double>(u_a, u_b) << endl; std::cout << Max<float>(int_a, int_b) << endl; return 0; }
当使用函数模板实参时,函数模板实参按照从左到右的顺序匹配函数模板参数列表中的模板参数;函数模板参数列表中未匹配的模板参数根据函数模板的实参推断进行匹配
六、函数重载与模板
函数模板可以被普通非模板函数或者另一个模板函数重载,重载的函数具有相同的名字且具有不同数量或者类型的参数;
对于某个函数调用,如果被调用函数被多个普通非模板函数和多个模板函数重载,那么其函数匹配规则将会变得复杂;
涉及到函数模板的函数匹配规则如下:
(1)确定候选函数集合:所有能够成功进行形参与实参匹配的普通非模板函数与所有模板实参推断成功的模板函数;
(2)函数匹配程度排序:可行函数(非模板函数与模板函数)按照类型转换排序;
(3)如果存在某一个函数比其他函数提供更好的匹配,则选择调用该函数;否则,按照以下规则选择匹配函数:
<1>同样好的匹配函数中有非模板函数,优先选择;
<2>同样好的匹配函数中没有非模板函数,但在所有模板函数中存在一个模板函数比其他函数更加特例化,则选择该模板函数;
<3>否则,匹配失败;