用可变模板扩展
以下3个gun
function的区别是什么?
template <class... Ts> void fun(Ts... vs) { gun(A<Ts...>::hun(vs)...); gun(A<Ts...>::hun(vs...)); gun(A<Ts>::hun(vs)...); }
我对使用一个具体例子解释这三个调用的答案感兴趣。
本来我只是从字面上回答了这个问题,但我想稍微扩展一下,以便更深入地解释如何将包扩展到什么地方。 无论如何,我都是这么想的。
任何立即紧接着椭圆形的包装都已经扩展到位。 所以A<Ts...>
相当于A<T1, T2, ..., TN>
, hun(vs...)
与hun(v1, v2, ..., vn)
同样相等。 当它变得复杂的时候,而不是一个包,然后是省略号((expr)...)
。 这将扩展到(expr1, expr2, ..., exprN)
,其中expri
引用原始expression式,其中任何包由第i
个版本replace。 所以如果你有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
“in place”,然后有一个expression式扩展到最后一个省略号,所以这个叫:
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
“就地”,但是我们没有任何的包可以扩展到最后的椭圆。
当Ts是T,U和vs是t时,他们如何扩展:
gun(A<Ts...>::hun(vs)...) -> gun(A<T, U>::hun(t), A<T, U>::hun(u)) gun(A<Ts...>::hun(vs...)) -> gun(A<T, U>::hun(t, u)); gun(A<Ts>::hun(vs)...) -> gun(A<T>::hun(t), A<U>::hun(u))
还有一个你没有提到的案例:
gun(A<Ts>::hun(vs...)...) -> gun(A<T>::hun(t, u), A<U>::hun(t, u))
如果你在VS14中运行下面的代码,你会得到这个输出:
calling gun(A<Ts...>::hun(vs)...); struct A<int,double>::hun(double); struct A<int,double>::hun(int); gun(struct A<int,double>, struct A<int,double>); calling gun(A<Ts...>::hun(vs...)); struct A<int,double>::hun(int, double); gun(struct A<int,double>); calling gun(A<Ts>::hun(vs)...); struct A<double>::hun(double); struct A<int>::hun(int); gun(struct A<int>, struct A<double>); calling gun(A<Ts>::hun(vs...)...); struct A<double>::hun(int, double); struct A<int>::hun(int, double); gun(struct A<int>, struct A<double>);
码:
#include <iostream> #include <typeinfo> using namespace std; void printTypes() {} template<typename T, typename... Ts> void printTypes(T, Ts... vs) { cout << typeid(T).name() << (sizeof...(Ts) ? ", " : ""); printTypes(vs...); } template<typename... Ts> struct A { template<typename... Us> static auto hun(Us... vs) { cout << " " << typeid(A).name() << "::hun("; printTypes(vs...); cout << ");" << endl; return A{}; } }; template<typename... Ts> void gun(Ts... vs) { cout << " gun("; printTypes(vs...); cout << ");" << endl; } template<typename... Ts> void fun(Ts... vs) { cout << "calling gun(A<Ts...>::hun(vs)...);" << endl; gun(A<Ts...>::hun(vs)...); cout << "calling gun(A<Ts...>::hun(vs...));" << endl; gun(A<Ts...>::hun(vs...)); cout << "calling gun(A<Ts>::hun(vs)...);" << endl; gun(A<Ts>::hun(vs)...); cout << "calling gun(A<Ts>::hun(vs...)...);" << endl; gun(A<Ts>::hun(vs...)...); } int main() { fun(1, 2.0); }