元组到参数包

问题描述 投票:4回答:2

以下代码来自用户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:这种技术可以归类为模板元编程吗?

c++ templates c++11 variadic-templates
2个回答
13
投票

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

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(),a.k.a。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),

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


1
投票

使用C ++ 17,您可以使用“if constexpr”来创建序列包装器:

template <int indxMax, template <int... > class spack, int ... seq>
constexpr auto get_seq17()
{
    static_assert(indxMax >= 0, "Sequence size must be equal to or greater than 0!");
    if constexpr (indxMax > 0)
    {
        typedef decltype(spack<indxMax, seq...>{}) frst;
        constexpr int next = indxMax - 1;
        return get_seq17<next, spack, indxMax, seq...>();
    }
    else
    {
        return spack<indxMax, seq...>{};
    }
}

template <int indxMax, template <int...> class pack>
struct seq_pack
{
    typedef decltype(get_seq17<indxMax, pack>()) seq;
};


//creating a sequence wrapper
template <int ... seq>
struct seqpack {};

//usage
seq_pack<4, seqpack>::seq; //seqpack<0, 1, 2, 3, 4> 

虽然这个实现更容易理解,但最好使用std::make_index_sequence<Size>,因为Julius在下面的评论中提到过。

© www.soinside.com 2019 - 2024. All rights reserved.