1.函数模板默认模板参数简介
函数模板与类模板在 C++98 一起被引入,因种种原因,类模板可以拥有默认模板参数,而函数模板不可以。从 C++11 开始,这个限制被解除了,即函数模板同样可以拥有默认模板参数。默认模板参数的用法与函数的默认参数类似,考察如下示例:
#include <iostream>
using namespace std;
//函数默认参数
void testFunc(int param=4)
{
cout<<"param="<<param<<endl;
}
//类模板默认模板参数
template<typename T=int> class TestClass
{
public:
static void _printTypeName()
{
cout<<"T="<<typeid().name()<<endl;
}
};
//函数模板的默认模板参数,C++11 开始支持
template<typename T=int> void testTemplateFunc(T param)
{
cout<<"TemplateFunc's param="<<param<<endl;
}
int main()
{
testFunc();
TestClass<>::_printTypeName();
testTemplateFunc<>(4);
}
编译运行输出:
param=4
T=int
TemplateFunc's param=4
在对函数模板 testTemplateFunc 进行显示调用时,并没有指明模板参数,而是使用默认的模板参数 int,可以正确编译并运行输出预期结果。
2.函数模板默认模板参数的特点
函数模板默认模板参数的用法虽然与类模板默认模板参数和函数默认参数的用法类似,但是有一个显著的特点,即当函数模板拥有多个默认模板参数时,其出现的顺序可以任意,不需要连续出现在模板参数的最后面。考察如下示例:
void testFunc(int param1=4,int param2){} //编译失败
template<typename T1=int,typename T2> class TestClass{}; //编译失败
template<typename T1=int,typename T2> void testTemplateFunc(T1 param,T2 param2){} //编译成功
从上面的代码可以看出,不按照从右往左指定函数的默认参数和类模板的默认模板参数均导致编译错误,而在C++11中,函数模板的默认模板参数出现的位置则比较灵活,可以出现在任意位置。
2.3 函数模板的参数推导规则
函数模板的参数推导规则是如果能够从函数实参中推导出类型的话,则函数模板的默认模板参数则不会被使用,反之,默认模板参数则可能被使用。考察如下示例:
template<typename T,typename U=double>
void testTemplateFunc(T t=0,U u=0)
{
cout<<"t="<<t<<" u="<<u<<endl;
}
int main()
{
testTemplateFunc(4,'a'); //调用testTemplateFunc<int,char>(4,'a')
testTemplateFunc(4); //调用testTemplateFunc<int,double>(4,0)
//testTemplateFunc(); //编译失败
testTemplateFunc<int>(); //调用testTemplateFunc<int,double>(0,0)
testTemplateFunc<int,char>(); //调用testTemplateFunc<int,char>(0,0)
}
程序编译运行输出:
t=4 u=a
t=4 u=0
t=0 u=0
t=0 u=
函数模板的模板参数是由函数的实参推导而来,因此函数调用testTemplateFunc(4)
是对函数模板实例化出的模板函数的调用,即testTemplateFunc<int,double>(4,0)
,其中第二个模板参数U使用了默认的模板类型参数double,实参则使用了默认参数0。同理,函数调用testTemplateFunc<int>()
最终的调用是testTemplateFunc<int,double>(0,0)
。而函数调用testTemplateFunc()
则因为无法推导出第一个模板参数T,导致编译出错。
从上面的例子也可以看出,因为函数模板的模板参数是由函数的实参推导而来,所以默认模板参数通常需要跟默认函数参数一起使用,不然默认模板参数的存在将没有意义。
参考文献
[1] 深入理解C++11[M].2.11模板函数的默认模板参数