Tuple to parameter pack

后端 未结 2 562
情深已故
情深已故 2020-12-24 08:48

This below code from user Faheem Mitha, is based on user Johannes Schaub - litb\'s answer in this SO. This code perfectly does what I seek, which is conversion of a tu

相关标签:
2条回答
  • 2020-12-24 09:08

    Let's look at what happens here:

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

    The first one is a generic template, the second one is a specialization that applies when the first template parameter is 0.

    Now, take a piece of paper and pencil, and write down how

     gens<3>
    

    gets defined by the above template. If your answer was:

     struct gens<3> : public gens<2, 2>
    

    then you were right. That's how the first template gets expanded when N is "3", and ...S is empty. gens<N - 1, N - 1, S...>, therefore, becomes gens<2, 2>.

    Now, let's keep going, and see how gens<2, 2> gets defined:

     struct gens<2, 2> : public gens<1, 1, 2>
    

    Here, in the template expansion, N is 2, and ...S is "2". Now, let's take the next step, and see how gens<1, 1, 2> is defined:

     struct gens<1, 1, 2> : public gens<0, 0, 1, 2>
    

    Ok, now how does gens<0, 0, 1, 2> gets defined? It can now be defined by the specialization:

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

    So, what happens with struct gens<0, 0, 1, 2> here? Well, in the specialization, "S..." becomes "0, 1, 2", so this becomes, in a manner of speaking:

     struct gens<0, 0, 1, 2> {
    
       typedef seq<0, 1, 2> type;
    
     }
    

    Now, keep in mind that all of these publicly inherit from each other, "elephant-style", so:

     gens<3>::type
    

    ends up being a typedef declaration for

     struct seq<0, 1, 2>
    

    And this is used, by the code that follows to convert the tuple into a parameter pack, using another template:

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

    ...Args are the tuple parameters. So, if there are three elements in the tuple, sizeof(...Args) is 3, and as I've explained above, gens<sizeof...(Args)>::type() becomes gens<3>::type(), a.k.a. seq<0, 1, 2>().

    So, now:

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

    The S... part becomes "0, 1, 2", so the

    std::get<S>(params)...
    

    Becomes a parameter pack that gets expanded to:

    std::get<0>(params), std::get<1>(params), std::get<2>(params),
    

    And that's how a tuple becomes a parameter pack.

    0 讨论(0)
  • 2020-12-24 09:09

    With C++17 you can use "if constexpr" to create a sequence wrapper:

    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> 
    

    Though this implementation is easier to understand, it is preferable to use std::make_index_sequence<Size> as Julius has mentioned in the comments below.

    0 讨论(0)
提交回复
热议问题