C++11 --可变参数模版
文章目录
- 1. 模版和函数参数包
- 2. 展开参数包
- 3. 在可变参数模版函数中使用递归
可变参数模版让你可以创建可以接受可变参数的函数模板和类模板。
例如:要编写一个函数,它可以接受任意数量的参数,参数的类型只需是cout能够显示的即可,并将参数显示为用逗号分隔的列表。
int n = 14;
double x = 2.71828;
string mr = "hello world";
show_list(n, x);
show_list(x* x, '!', mr);
这里的目标是,定义show_list(),让让上述代码能够通过编译并生成如下输出:
14, 2.71828;
7.38905, !,hello world
要创建可变参数模版,需要理解几个要点:
- 模版参数包
- 函数参数包
- 展开参数包
- 递归
1. 模版和函数参数包
为理解参数包的工作原理,首先来看一个简单的模版函数,它显示一个只有一项的列表:
template<typename T>
void show_list(T value)
{
cout << value << ",";
}
在上述定义中,右两个参数列表,模版参数列表只包含T,而函数参数列表只包含value。下面的函数调用将模版参数列表中的T设置为double,将函数参数列表中value设置为2.15.
show_list0(2.15);
C++11提供了一个用省略号标识的元运算符,能够声明标识模版参数包的标识符。模版参数包基本上是一个类型列表。同时还可以声明函数参数包的标识符。而**函数参数包基本上是一个值列表**。语法如下:
template<typename... Args>
void show_list1(Args... args)
{
//
}
其中,Args是一个模版参数包,args是一个函数参数包,与其他参数名一样,可将这些参数包的名称指为任符合C++标识符规则的名称。Args 和 T 的差别在于,T 与 一种类型匹配,Args与任意数量(包括0)的类型匹配。
下面的程序调用:
show_list1('s', 80, "sweet", 4.5);
在这种情况下,参数包Args包含于函数调用的参数匹配类型:char、int、const char*、double.
下面的代码,指出value的类型为T
void show_list0(T value)
同样,下面的代码指出args 的类型为 Args
void show_list1(Args... args)
在这种情况下,函数参数包args包含值:‘s’, 80, “sweet”, 4.5
这意味着**函数参数包args包含的值列表 和 模版参数包Args包含的类型列表相匹配—无论是类型还是数量**。
2. 展开参数包
但是函数如何访问这些包的内容呢?索引功能不能使用,即不能通过Args[2]来访问包中的第3个类型。相反,可将省略号放在函数参数包名的右边,将参数包展开,例如:下面有缺陷代码:
template<typename... Args>
void show_list1(Args... args)
{
show_list1(args...);
}
这是什么意思?有什么缺陷?有如下函数调用:
show_list1(5,'L',0.5);
这将把 5,‘L’,0.5 封装到args中。在该函数内部,下面的调用:
show_list1(args...);
将展开为:
show_list1(5,'L',0.5);
也就是说,args 被替换为3个存储在args中的值,因此,表示法 args… 展开为一个函数参数列表。
缺陷:该函数调用与原函数调用相同,因此它将使用相同的参数不断的调用自己,导致无限递归。
3. 在可变参数模版函数中使用递归
缺陷如何解决?
核心理念:将函数参数包展开,对列表中的第一项进行处理,再将余下的内容传递给递归调用,以此例推,直到列表为空。
template<typename T,typename... Args>
void show_list3(T value, Args... args)
对于上述的定义:show_list3()的第一个参数决定了T 和value的值,而其他参数决定了Args 和args 的值。这让函数能够对value进行处理,如:显示它。然后递归调用show_list3()调用,并以args… 的方式将其它实参传递给它。每次递归调用都将显示一个值,并传递缩小了的列表。直到列表为空。
void show_list3()
{
}
template<typename T,typename... Args>
void show_list3(T value, Args... args)
{
cout << value << ",";
show_list3(args...);
}
int main()
{
int n = 14;
double x = 1.54;
string mr = "hello world!";
show_list3(n, x, mr);
return 0;
}
/*输出结果:
14,1.54,hello world!,
*/
函数调用:show_list3(n, x, mr);
第一个实参T类型为:int,value为 14,其他二中类型(double,stirng)放入Args包中,而其他二个值(1.54,“hello world!”)放入args包中。
接下来调用:
show_list3(args...);
展开后:
show_list3(1.54,"hello world!");
前面说过,列表每次减少一项,这次T为:double,value为:1.54,而余下的一种类型和一个值分别被包装到Args和args中。下一次递归再将包缩小。最后,当args为空时,将调用不接受任何参数的show_list3(),导致结束。
优化:
- 去掉结果中最后一个逗号。
- 将值传递改为引用传递。—提高效率
void show_list3()
{
}
template<typename T>
void show_list3(const T& value)
{
cout << value << '\n';
}
template<typename T,typename... Args>
void show_list3(const T value, const Args... args)
{
cout << value << ",";
show_list3(args...);
}
int main()
{
int n = 14;
double x = 1.54;
string mr = "hello world!";
show_list3(n, x, mr);
return 0;
}
/*输出结果:
14,1.54,hello world!
*/
全文完!