目录
🌈前言
本篇文章进行C++11中可变参数模板和lambda表达式的学习!!!
🚁1、可变参数模板
-
“可变参数模板”是支持任意数量的参数的类模板或函数模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进
-
然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。现阶段,我们掌握一些基础的可变参数模板特性就够我们用了
可变参数的函数模板:
// Args是一个模板参数包,args是一个函数参数包
// 声明一个Args... args参数包,说明这个参数包包含0到任意个模板参数
template <class ...Args>
void ShowList(Args... args)
{
}
可变参数模板以两种方式使用省略号:
-
在参数名的左边,表示”参数包“,它里面包含0到N(N>=0)个”参数包“
-
在参数名的右边,表示”将参数包扩展为单独的名称(类型对象)“
-
我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数
-
由于语法不支持使用args[i]这样方式获取可变参数,所以我们只能用一些奇怪的方式且难理解来一一获取参数包的值
🚂1.1、递归函数方式展开参数包
这种解包方式需要通过”解包终止函数/函数模板“来解决
namespace VT
{
// 终止函数
template <typename T>z
void Test(const T& t)
{
cout << t << endl;
}
// 展开函数
template <typename T, typename ...Args>
void Test(const T& val, Args... args)
{
// sizeof ...(args)是求函数参数包的个数 -- 每次进入函数模板解一个,所以一开始为n-1个
cout << sizeof ...(args) << " " << val << endl;
// 使用递归展开函数参数包 -- 一开始递归调用自身,最后没有参数包为0时,调用另外一个模板函数
Test(args...);
}
void Test()
{
Test(1);
cout << endl;
Test(1, 'a', 1.0, "abcdef");
cout << endl;
}
}
注意:sizeof …(agrs)是求函数形参的参数包个数,一开始进入函数模板时已经解了一个,所以是3
🚃1.2、逗号表达式展开参数包
逗号表达式:对”对象“赋值时,逗号表达式会按顺序执行逗号前面的表达式,但是只会取最后一个逗号后面的值(int a = (1, 2, 3),取3)
template <typename T>
void PrintArg(const T& t)
{
cout << t << endl;
}
// 展开函数
template <typename T, typename ...Args>
void ShowList(const T& t, Args... args)
{
cout << t << endl;
// 逗号表达式从左到右执行,只取最后一个,后面的值,所以Array里面存储的全是0,0之前是展开参数包
int Array[] = {
(PrintArg(args), 0)... };
}
void Test()
{
ShowList(1, 1.2, 'a', "abcdef");
cout << endl;
}
代码解析:
-
这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, PrintArg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数
-
这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式,所以展开一个参数包就把0赋值给了Array数组,数组有多少个0就代表有多少个参数
expand函数体中逗号表达式解析:
-
(PrintArg(args), 0),也是按照这个执行顺序,先执行PrintArg(args),再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性——初始化列表,通过初始化列表来初始化一个变长数组, {(PrintArg(args), 0)…}将会展开成((printarg(arg1),0),(PrintArg(arg2),0), (PrintArg(arg3),0), etc… ),最终会创建一个元素值都为0的数组int arr[sizeof…(Args)]
-
由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分PrintArg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包
不能在类模板中判断递归结束条件,因为模板是编译时才实例化的,并不会执行判断条件(运行时判断)
template <typename T, typename ...Args>
void ShowList(const T& t, Args... args)
{
cout << t << endl;
if (sizeof ...(args) == 0)
{
return;
}
int Array[] = {
(ShowList(args)