[TOC]
最近,看C++11相关的东西,看到模板变长参数的时候,关于变长参数的代码都看不懂了~
变长参数模板
解包的正确姿势
参考[1][3]中说变长参数模板如何解包的问题,其中一种方法是用逗号表达式+初始化列表的方式,参考[3]
template <class T>
void printarg(T t)
{
cout << t << endl;
}
template <class ...Args>
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 <typename T>
T func(T value)
{
return value;
}
template <typename T, typename ... U>
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<class ...Args>
void f(Args... args) {
auto lm = [&, args...] { return g(args...); };
lm();
}
2.Brace-enclosed initializers。 解包的正确姿势 中就是属于这种情况 参考[1]中的代码也属于这种情况。开始以为是lambda那种情况,细细分析一下觉得还是属于初始化列表环境。下面是逐行解析:
template<typename T, typename... Ts>
auto printf3(T value, Ts... args) {
std::cout << value << std::endl;
(void) std::initializer_list<T>{([&args] {
std::cout << args << std::endl;
}(), value)...};
}
(1)保证环境正确:在*std::initializer_list
([&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]形参包