一、什么是函数模板
函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。
二、常规模板、模板的显示具体化和模板的实例化
2.1 常规模板、模板的显示具体化和模板的实例化
- 常规模板
//常规模板
template<typename T>
T fun(T t){
return t;
}
- 模板的显示具体化
- 为什么要具体化 ?
显示具体化的模板就是为模板指明型别,能够针对某种具体的类型使用不同的算法。 - 如何具体化?
1)fun中的 int 表明了要将类型参数 T 具体化为 int 类型,原来使用 T 的位置都应该使用 int 替换,包括返回值类型、形参类型、局部变量的类型。
2)模板头写作template<> 。fun只有一个类型参数 T,并且已经被具体化为 int了,这样整个模板就不再有类型参数了,类型参数列表也就为空了。
3)fun中的int是可选的,因为函数的形参已经表明,这是 int 类型的一个具体化,编译器能够逆推出 T 的具体类型。
- 为什么要具体化 ?
//显示具体化的模板
//写法1
template<>
int fun<int>(int t){
return t;
}
//写法2
template<>
int fun(int t){
return t;
}
- 模板的实例化
- 隐式实例化
模板的实例化是按需进行的,用到哪个类型就生成针对哪个类型的函数。
在使用模板函数时,不存在指定类型的模板函数的实体时,由编译器根据指定类型参数隐式生成模板函数的实体称之为模板的隐式实例化。
- 隐式实例化
- 显式实例化
格式:template 函数返回类型 函数模板名<实际类型列表>(函数参数列表)
template void func<int>(const int&); //实例化不用函数实现么?
2.2 优先级
非模板函数 > 具体化模板 > 常规模板
2.3 函数模板的调用
- 隐式模板实参调用
在发生函数模板的调用时,不显示给出模板参数而经过参数推演,称之为函数模板的隐式模板实参调用(隐式调用)。如:
template <typename T> void func(T t){
cout << t << endl;
}
func(5);//隐式模板实参调用
- 显式模板实参调用
在发生函数模板的调用时,显示给出模板参数而不需要经过参数推演,称之为函数模板的显示模板实参调用(显示调用)。
显示模板实参调用在参数推演不成功的情况下是有必要的。
#include <iostream>
using namespace std;
template <typename T> T Max(const T& t1,const T& t2){
return (t1>t2)?t1:t2;
}
int main(){
int i=5;
//cout<<Max(i,'a')<<endl; //无法通过编译
cout<<Max<int>(i,'a')<<endl; //显示调用,通过编译
}
三、模板何时需要显示调用
1.1 函数模板语法
template < 形参列表 > 函数声明
template <typename T>
T fun(T t) {
return T();
}
1.2 模板何时需要指定模板形参
- 涉及到模板型别推导时,不需要指定模板形参(也可以指定,但是指定的和推推导的结果不一样会报错),因为模板形参可以被推导出来。如:std::move。
//涉及到模板型别推导
template <typename T>
T fun(T t) {
return T();
}
func(1);
- 未涉及到模板型别推导时,必须指定模板形参,如:std::forward。
//未涉及到模板型别推导
template <typename T>
T fun() {
return T();
}
func<int>(1);