以下来自用户Faheem Mitha的代码基于用户Johannes Schaub-litb在此SO中的回答。这段代码完美地实现了我所追求的,即将tuple转换为参数包,但是我对这段代码的理解不够充分,因此我想我将进行一个新的讨论,该模板可能有助于对像我这样的新手进行元编程。因此,请原谅重复的过帐。

现在进入代码

#include <tuple>
#include <iostream>
using std::cout;
using std::endl;

template<int ...> struct seq {};

template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { };

template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

double foo(int x, float y, double z)
{
    return x + y + z;
}

template <typename ...Args>
struct save_it_for_later
{
    std::tuple<Args...> params;
    double(*func)(Args...);

    double delayed_dispatch()
    {
        return callFunc(typename gens<sizeof...(Args)>::type()); // Item #1
    }

    template<int ...S>
    double callFunc(seq<S...>)
    {
        return func(std::get<S>(params) ...);
    }
};

int main(void)
{
    std::tuple<int, float, double> t = std::make_tuple(1, (float)1.2, 5);
    save_it_for_later<int, float, double> saved = { t, foo };
    cout << saved.delayed_dispatch() << endl;
    return 0;
}

我完全被上面的#1所困扰:
  • typename在该行上有什么作用?
  • 我了解gens<sizeof...(Args)>::type()将扩展为gens<3>::type(),但这似乎既不匹配template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { };也不匹配template<int ...S> struct gens<0, S...>。我显然不明白要点,如果有人可以解释这里发生的事情,我将很高兴。

  • 我确实知道callFunc会以callFunc(seq<0,1,2>)的形式被调用,并且此方法的return语句本身会扩展为return func(std::get<0>(params), std::get<1>(params), std::get<2>(params),这就是使该方案起作用的原因,但是我无法确定如何生成此seq<0,1,2>类型。

    注意:不能使用std::index_sequence_for,我的编译器不支持C++ 14功能。

    PS:这项技术可以归类为模板元编程吗?

    最佳答案

    让我们看看这里发生了什么:

    template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { };
    
    template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
    

    第一个是通用模板,第二个是当第一个模板参数为0时应用的特化。

    现在,拿一支纸和铅笔,写下如何
     gens<3>
    

    由上述模板定义。如果您的答案是:
     struct gens<3> : public gens<2, 2>
    

    那你是对的。这就是N为“3”且...S为空时第一个模板的扩展方式。因此,gens<N - 1, N - 1, S...>变为gens<2, 2>

    现在,让我们继续前进,看看如何定义gens<2, 2>:
     struct gens<2, 2> : public gens<1, 1, 2>
    

    在此,在模板扩展中,N为2,而...S为“2”。现在,让我们进行下一步,看看如何定义gens<1, 1, 2>:
     struct gens<1, 1, 2> : public gens<0, 0, 1, 2>
    

    好的,现在如何定义gens<0, 0, 1, 2>?现在可以通过特化来定义:
     template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
    

    那么,这里的struct gens<0, 0, 1, 2>会发生什么?好吧,在特化中,“S ...”变成“0,1,2”,因此从某种意义上说变成:
     struct gens<0, 0, 1, 2> {
    
       typedef seq<0, 1, 2> type;
    
     }
    

    现在,请记住,所有这些都是“大象风格”彼此公开继承的,因此:
     gens<3>::type
    

    最终成为typedef声明
     struct seq<0, 1, 2>
    

    然后,以下代码使用另一个模板将其用于将元组转换为参数包:
    double delayed_dispatch()
    {
        return callFunc(typename gens<sizeof...(Args)>::type()); // Item #1
    }
    
    ...Args是元组参数。因此,如果该元组中包含三个元素,则sizeof(...Args)为3,正如我在上文中所解释的,gens<sizeof...(Args)>::type()变为gens<3>::type(),又称为seq<0, 1, 2>()

    因此,现在:
    template<int ...S>
    double callFunc(seq<S...>)
    {
        return func(std::get<S>(params) ...);
    }
    
    S...部分变为“0,1,2”,因此
    std::get<S>(params)...
    

    成为参数包,并扩展为:
    std::get<0>(params), std::get<1>(params), std::get<2>(params),
    

    这就是元组变成参数包的方式。

    关于c++ - 元组到参数包,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36612596/

    10-09 17:14