C++ 变长参数解包
最近,看C++11相关的东西,看到模板变长参数的时候,关于变长参数的代码都看不懂了~
变长参数模板
解包的正确姿势
参考[1][3]中说变长参数模板如何解包的问题,其中一种方法是用逗号表达式+初始化列表的方式,参考[3]
template void printarg(T t) { cout << t << endl; } template void expand(Args... args) { int arr[] = {(printarg(args), 0)...}; } expand(1,2,3,4);
其中 {(printarg(args), 0)…}
展开为 (printarg(arg1), 0), (printarg(arg2), 0), (printarg(arg3), 0), (printarg(arg4), 0)
。
如果,我把调用改成 expand(1, 2.987, 3, “abc”);
,结果为
1 2.987 3 abc
解包的错误姿势
参考[2]代码
template T func(T value) { return value; } template void g(T value, U ...u) { func(u)...; // error C3520: “u”: 必须在此上下文中扩展参数包 } g(1.2, 3.4, 5.6);
可以看到,用法 func(u)…
显示的编译错误
error C3520: “u”: 必须在此上下文中扩展参数包
参考[2]中的有网友给出了一些解决办法
int a[] = {0, (func(u), 0)...};
小结
这里我不懂的地方都是属于变长参数范围,总的来说是这个点自己不清楚。所以,看代码就很疑惑。关于我不懂的地方,现在清楚了一点
-
1.这里的(args)…,就是一个 模式
后面跟着冒号的表达式,表示对这个 模式
进行解包 - 2.解包这个语法使用有限定环境,如果不在限定环境内使用,就会报错 必须在此上下文中扩展参数包
变长参数解包
参考[4][5],这篇非常值得拜读!还有些地方虽然没看懂,但是收获颇丰,基本上我的疑惑都是从这里得到解答了。
列两个模板变长参数解包相关的环境
1. Lambda captures
template void f(Args... args) { auto lm = [&, args...] { return g(args...); }; lm(); }
2. Brace-enclosed initializers
。
解包的正确姿势 中就是属于这种情况
参考[1]中的代码也属于这种情况。开始以为是lambda那种情况,细细分析一下觉得还是属于初始化列表环境。下面是逐行解析:
template auto printf3(T value, Ts... args) { std::cout << value << std::endl; (void) std::initializer_list{([&args] { std::cout << args << std::endl; }(), value)...}; }
(1)保证环境正确:在*std::initializer_list *后面的花括号环境中,可以使用包展开。
(2)包展开语法:模式…,这里的模式是小括号中的部分。一个lambda表达式和value组成的逗号表达式
([&args] { std::cout << args << std::endl; }(), value)
(3)假设三个参数展开为: ([&arg1] {std::cout « arg1 « std::endl;}(), value), ([&arg2] {std::cout « arg2 « std::endl;}(), value), ([&arg3] {std::cout « arg3 « std::endl;}(), value)
。
这也就是我最终认为,这个例子里面用到的是初始化列表展开,不是属于lambda环境展开。因为初始化列表展开之后到lambda捕获的时候已经不是一个pack了,而是展开之后逐个的元数了。
参考
[1] modern-cpp-tutorial
[2] C++可变参数模板展开
[3] 泛化之美–C++11可变模版参数的妙用
[4] Parameter pack
[5] 形参包