最初我只是从字面上回答了这个问题,但我想稍微扩展一下,以便更彻底地解释什么包是如何扩展成什么的。无论如何,这就是我思考事情的方式。
任何紧跟省略号的包都只是在原地展开。所以A<Ts...> 等价于A<T1, T2, ..., TN> 并且hun(vs...) 同样等价于hun(v1, v2, ..., vn)。它变得复杂的地方是当你得到类似((expr)...)的东西而不是一个包后跟省略号时。这将扩展为(expr1, expr2, ..., exprN),其中expri 指的是原始表达式,其中任何包都替换为ith 版本。所以如果你有hun((vs+1)...),那就变成hun(v1+1, v2+1, ..., vn+1)。更有趣的是expr 可以包含多个包(只要它们的大小相同!)。这就是我们实现标准完美转发模型的方式;
foo(std::forward<Args>(args)...)
这里expr 包含两个包(Args 和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 和vs 都扩展为“就地”,但是我们没有任何包可以扩展最终的椭圆。