Search code examples
c++templatesvariadic-templates

Differentiate between two pack expansions


Is there a way to explicitly specify which ... refers to which pack expansion? In my code I have two pack expansions that I want to apply at different levels:

template<typename T, int N>
struct MyArr
{
    T e[N];

    constexpr T& operator[](int i) { return e[i]; }
    constexpr const T& operator[](int i) const { return e[i]; }

    MyArr() : e{} {}
    template<typename ...type_pack>
    MyArr(const type_pack&... pack) : e{pack...}
    {
        static_assert(sizeof...(pack)==N, 
        "Argument count must match the size.");
    }
};

template<typename type_lhs, typename type_rhs>
auto add(const type_lhs& lhs, const type_rhs& rhs)
{
    return lhs + rhs;
}

template<int ...I, typename type_head, typename ...type_pack, int N, typename Function>
auto apply(Function&& op, 
const MyArr<type_head,N>& head, const MyArr<type_pack,N>&... pack)
{
    return MyArr<type_head,N>((op(head[I],(pack[I])...))...);
    //                              expand pack[I]- ^ ,  ^ - expand I...
};

int main()
{
    MyArr<int,3> a(1,2,3);
    return apply<0,1,2>(add<int,int>, a, a);
}

Essentially, I want to get:

(op(head[0], get<0>(pack)[0], ..., get<M-1>(pack)[0]),
 ..., 
 op(head[N-1], get<0>(pack)[N-1], ..., get<M-1>(pack)[N-1]))

Thanks to OznOg's advice I got it to work through creating a function in the middle:

template<int ...I, typename type_head, typename ...type_pack, int N, typename Function>
auto apply(Function&& op, 
const MyArr<type_head,N>& head, const MyArr<type_pack,N>&... pack)
{
    auto op2 = [&](int i) { return op(head[i], pack[i]...);};
    return MyArr<type_head,N>(op2(I)...);
};

Solution

  • In this particular case, the only way I see is the use of an helper function (getVal(), in the following example)

    template <int I, typename type_head, typename ...type_pack, int N,
              typename Function>
    auto getVal (Function&& op, MyArr<type_head,N> const & head,
                 MyArr<type_pack,N> const & ... pack)
     { return op(head[I], pack[I]...); }
    
    template <int ... Is, typename type_head, typename ...type_pack, int N,
              typename Function>
    auto apply (Function && op, MyArr<type_head,N> const & head,
                MyArr<type_pack,N> const &... pack)
     { return MyArr<type_head,N>{ getVal<Is>(op, head, pack...)... }; }
    

    The problem is that you have

    (pack[I])...
    

    so there is no way (as far I know) to say that the expansion is to be applied to pack and not to I.

    With the intermediate function

    //.......................VVV  expand pack
    getVal<Is>(op, head, pack...)...
    //...........................^^^  expand Is
    

    you can use parentheses to separate the levels.

    But you have to separate pack and Is.