用可变模板扩展

以下3个gunfunction的区别是什么?

 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包含两个包( Argsargs都是包),扩展“迭代”

 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...)...); 

这不会编译。 我们扩大了Tsvs “就地”,但是我们没有任何的包可以扩展到最后的椭圆。

当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); }