深入探索C++中模板参数的自动推导
利用模板参数推导时需要注意以下几点:
- 编译器只根据函数调用时给出的实参列表来推导模板参数值,与函数参数类型无关的模板参数其值无法推导。
- 与函数返回值相关的模板参数其值也无法推导。所有可推导模板参数必须是连续位于模板参数列表尾部,中间不能有不可推导的模板参数。
下面是一个简单的程序。
#include<iostream>template<typename T0,typename T1,typename T2,typename T3,typename T4>
T2 func(T1 v1,T3 v3,T4 v4);int main()
{double sv2;using namespace std;sv2 = func<double, int, int>(1, 2, 3);cout << "\tsv2:" << sv2 << endl;sv2 = func<double, int, int>(1, 2, 3);cout << "\tsv2:" << sv2 << endl;sv2 = func<double, int, int>(1, 0.1, 0.1);cout << "\tsv2:" << sv2 << endl;sv2 = func<int, double, double>(0.1, 0.1, 0.1);cout << "\tsv2:" << sv2 << endl;
}template<typename T0,typename T1,typename T2,typename T3,typename T4>
T2 func(T1 v1,T3 v3,T4 v4)
{T0 static sv0 = T0(0);T2 static sv2 = T2(0);std::cout << "\tvl:" << vl<< "\tv3:" << v3<< "\tv4:" << v4<< "\t|| sv0:" << sv0;T2 v2 = sv2;sv0 -= 1;sv2 -= 1;return v2;
}
在这个简单的程序中首先是一个函数模板 func 的声明。是的,模板与普通函数及类一样,可以先声明后实现。编译器在处理该模板声明
时,已知这个模板接受5个模板参数,依次为 T0 ~ T4,其中用于声明函数参数类型的模板参数为 T1、T3 和 T4。T2 用于声明函数的返回
值类型,T0 则与函数参数类型以及返回值类型都不相关。编译器在遇到 main 函数中对 func 函数模板的调用时,根本无法从实参类型推
导出 T0 的值,所以 T0 的值必须在模板实参列表中给定。至于 T2,虽然与函数返回值类型相关,粗略一想,似乎可以在某些情况下反推
出 T2 的值,例如:
double d = func<...>(...);
可否认为 T2 的值就应该是一个 double型呢?其实不然。由于 C++ 中存在内建自动类型转换机制,double 型变量不仅可以接受其他
double 型变量或常量的赋值,也可以接受诸如 int 型、char 型等多种类型变量常量的值,更何况还有用户自定义的类型转换。如果不在
模板参数列表中显式给定函数返回值类型,则以上代码就会产生歧义。况且有的时候函数返回值有可能被忽略。因此函数返回值类型不能
根据上下文推断,也就无法自动推导出与函数返回值相关的模板参数值,所以 T2 的值也必须在调用时给定。又因为 T2 必须给定而模板
形参与实参之间只能通过位置相关联,因此虽然 T1 的值可由函数实参类型推导出,也必须在模板实参列表中给出。很遗憾,C++ 中并不
能支持类似以下的方式来跳过对 T1 赋值:
sv0 = func<double, , int>(1, 2, 3); //语法错误!
所以,调用 func 时,模板实参列表中只可将T3 及 T4 省略,而 T0、T1 及 T2 不能省略。
该文章会更新,欢迎大家批评指正。
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
服务器课程:C++服务器