最初我只是字面地回答了这个问题,但我想对此进行一些扩展,以便更全面地解释什么包是如何扩展成什么的。无论如何,这就是我对事情的看法。
任何紧跟省略号的包都会就地展开。所以A<Ts...>
相当于A<T1, T2, ..., TN>
and hun(vs...)
同样相当于hun(v1, v2, ..., vn)
。事情变得复杂的是,当你得到的不是一个包后跟省略号时,你会得到类似的东西((expr)...)
。这将扩展为(expr1, expr2, ..., exprN)
where expri
指的是原始表达式,任何包替换为i
它的第一个版本。所以如果你有hun((vs+1)...)
,那就变成hun(v1+1, v2+1, ..., vn+1)
。更有趣的地方在于expr
可以包含多于一包(只要它们的尺寸相同!)。这就是我们实现标准完美转发模型的方式;
foo(std::forward<Args>(args)...)
Here expr
包含两包(Args
and args
都是包)并且扩展“迭代”两者:
foo(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), ..., std::forward<ArgN>(argN));
这种推理应该能够快速浏览您的案例,例如,当您致电时会发生什么foo(1, 2, '3')
.
第一个,gun(A<Ts...>::hun(vs)...);
扩大Ts
“就地”,然后有一个表达式来扩展最后一个省略号,所以这调用:
gun(A<int, int, char>::hun(1),
A<int, int, char>::hun(2),
A<int, int, char>::hun('3'));
第二个,gun(A<Ts...>::hun(vs...));
将两个包展开到位:
gun(A<int, int, char>::hun(1, 2, '3'));
第三个,gun(A<Ts>::hun(vs)...)
,同时展开两个包:
gun(A<int>::hun(1),
A<int>::hun(2),
A<char>::hun('3'));
[更新]为了完整性,gun(A<Ts>::hun(vs...)...)
会打电话:
gun(A<int>::hun(1, 2, '3'),
A<int>::hun(1, 2, '3'),
A<char>::hun(1, 2, '3'));
最后,还有最后一种情况需要考虑我们在省略号上过度使用的情况:
gun(A<Ts...>::hun(vs...)...);
这不会编译。我们同时扩展两者Ts
and vs
“就位”,但是我们没有任何包可以扩展为最后的椭圆。