函数模板是什么?
通用函数描述,使用泛型来定义函数,其中的泛型可以用具体的类型来替换,使用函数模板的编程有时候也叫做泛型编程。
知识总结
在标准的C++98添加关键字typename之前,C++使用关键字class来创建函数模板。
// 函数声明,声明时也要用模板的方式
template <typename T>
void Swap(T &a, T &b);
注意函数模板不能缩短可执行程序,最终的代码不包含任何模板,而只包含了为程序生成的实际函数。使用函数模板的好处是,使得生成多个函数定义更加的简单、可靠。
并非所有的模板参数都必须是模板参数类型。
模板函数也可以用来重载。
要注意函数模板的局限性,如对结构进行比较大小等操作时,模板函数可能无法处理某些类型。
具体化
可以提供一个具体化的定义,称为显示具体化。
.对于给定的函数名,可以有:
非模板函数
模板函数
显示具体化模板函数
以及他们的重载类型。
显示具体化的原型和定义应该以:
template<>
开头,并通过名称来指出类型。
优先级关系:
非模板函数 > 显示具体化函数
显示具体化函数 > 常规模板函数
template <typename T>
void Swap(T &, T &);
// 显示具体化函数的声明
template <> void Swap<job> (job &, job &);
// 等价于
template <> void Swap<> (job &, job &);
在代码中包含函数模板本身并不会生成函数定义,它只是一个用于函数定义的方案。
最初,编译器只能通过隐式实例化,来使用模板生产函数定义,但现在C++还允许使用显示实例化(explicit instantiation)。
// 显示实例化的声明
template void Swap<int> (int, int);
// 显示具体化的声明
template<> void Swap<int> (int &, int &);
template<> void Swap<> (int &, int &); // 等价
注意:视图在同一个文件(或转换单元)中使用同一种类型的显示实例和显示具体化将出错。
可以通过在程序中使用函数来创建显示实例化。
template<class T>
T Add(T a, T b)
{
return a + b;
}
int m = 6;
double x = 6.66666;
double ans = Add<double> (x, m);
隐式实例化、显示实例化和显示具体化统称为具体化(specialization) 。
重载解析
- 创建候选函数列表。
- 使用候选函数列表创建可行函数列表
- 确定是否有最佳的可行函数
在第一步中,锁定同名的函数和函数模板。
在第二步中,创建的都是参数数目正确的函数,有一个隐式转换序列,包括实参类型与形参类型完全匹配的情况。
第三步就是匹配,匹配成功,就可以使用他,匹配不成功,调用报错。
最佳匹配顺序
- 完全匹配,但是正常的函数要优于模板函数。
- 提升转换
- 标准转换
- 用户自定义的转化
错误情况
正常请胯下,如果有多个匹配的原型,则编译器无法完成重载解析过程。
如果没有最佳的可行函数,编译器会出现二义性的报错。
ambiguous——二义性
例外
但有些情况下,即使有两个函数完全匹配,却仍旧可以正常的执行。
例如,
- 执行非const数据的指针和引用,优先于const指针和引用。
- 非模板函数的完全匹配由于模板函数(包括显示具体化)的完全匹配。
- 如果两个完全匹配都是模板函数,则较具体的模板函数优先。即显示具体化优于隐式具体化。
最具体并不一定意味着显示具体化,而是值编译器推断使用哪种类型的时候,执行的转换最少。