使用可变参数模板进行扩展[重复]

Posted

技术标签:

【中文标题】使用可变参数模板进行扩展[重复]【英文标题】:Expansion with variadic templates [duplicate] 【发布时间】:2015-01-02 05:32:36 【问题描述】:

gun 函数的以下 3 次调用有什么区别?

template <class... Ts> void fun(Ts... vs) 
  gun(A<Ts...>::hun(vs)...);
  gun(A<Ts...>::hun(vs...));
  gun(A<Ts>::hun(vs)...);

我对使用特定示例解释这三个调用的答案感兴趣。

【问题讨论】:

玩枪的乐趣?? @0x499602D2 有趣的枪,伙计 一个类似的问题是这个***.com/questions/17652412/…,但下面的答案更彻底地澄清了我的问题,并且更具体 【参考方案1】:

最初我只是从字面上回答了这个问题,但我想稍微扩展一下,以便更彻底地解释什么包是如何扩展成什么的。无论如何,这就是我思考事情的方式。

任何紧跟省略号的包都只是在原地展开。所以A&lt;Ts...&gt; 等价于A&lt;T1, T2, ..., TN&gt; 并且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 包含两个包(Argsargs 都是包)并且扩展“迭代”两个包:

foo(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), ..., std::forward<ArgN>(argN));

这种推理应该可以让您快速浏览您的案例,例如,当您致电 foo(1, 2, '3') 时会发生什么。

第一个 gun(A&lt;Ts...&gt;::hun(vs)...);Ts 扩展为“就地”,然后有一个表达式可以扩展最后一个省略号,因此调用:

gun(A<int, int, char>::hun(1), 
    A<int, int, char>::hun(2), 
    A<int, int, char>::hun('3'));

第二个,gun(A&lt;Ts...&gt;::hun(vs...)); 扩展两个包:

gun(A<int, int, char>::hun(1, 2, '3'));

第三个,gun(A&lt;Ts&gt;::hun(vs)...),同时展开两个包:

gun(A<int>::hun(1), 
    A<int>::hun(2), 
    A<char>::hun('3'));

[更新] 为了完整起见,gun(A&lt;Ts&gt;::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 都扩展为“就地”,但是我们没有任何包可以扩展最终的椭圆。

【讨论】:

【参考方案2】:

当 Ts 为 T, U 且 vs 为 t, u 时,它们的扩展方式如下:

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

【讨论】:

非常感谢您的回答和代码示例!但是,我选择了上一个答案,因为它提供了更多详细信息。

以上是关于使用可变参数模板进行扩展[重复]的主要内容,如果未能解决你的问题,请参考以下文章

从可变参数模板中扩展的 decltype 继承

中继可变参数模板参数时出现 Visual Studio 错误 [重复]

为啥编译器不能通过逗号运算符扩展可变参数模板的参数?

参数包没有用“...”扩展——gcc 的另一个可变参数模板错误?

可变参数模板到数组访问的无递归扩展

重复的可变参数模板参数