Search code examples
c++templatesvariadic-templatesfold

C++ templated packs, fold twice


I have read some questions that are similar but I don't find the exact thing I am looking for.

In a purely mathematical way a list is defined recursively as: (head, rest).

Where head is the first element in the list and rest is a list. So for example (1,2,3,4) is represented as (1, (2,(3,(4,[])))) where [] is the empty list.

Then if we want to iterate over the list we can write a recursive function more or less like this:

iterate(list)
    head = list.head
    // do stuff and return if head is the empty element
    iterate(list.rest)

And if we want to iterate over every 2 elements we do:

pair_iterate(list)
        head1 = list.head
        head2 = list.rest.head
        // do stuff and return if head is the empty element
        iterate(list.rest.rest)

I am trying to get that second behaviour in C++.

In C++ 17, folds got introduced, so one can do something like this:

template<typename...types>
auto sum(types...values) {
  return (... + values);
}

But let's say we wanted the sum of the products of adjacent parameters, e.g sum(1,2,3,4) is 1*2 + 3*4.

In this case we need to "fold twice" to get the the 2 heads to perform the operation and pass the rest of the list. Similar to my pseudocode.

Does anyone have advice on how to get 2 folds in a row?

EDIT: I specifically want to do it with folds, i.e inside the function declaration without having to rely on recursive templated functions.


Solution

  • You can unpack the parameters 2 at a time, like this:

    template <typename T1, typename T2, typename ...Ts>
    auto sum_products(T1 t1, T2 t2, Ts ...ts)
    {
      return t1 * t2 + sum_products(ts...);
    }
    

    and provide a base case overload for no arguments:

    auto sum_products() { return 0; }
    

    and then use it like this:

    std::cout << sum_products(1,2,3,4);  // prints 14
    

    Here's a demo.

    Note that this will only work for an even number of arguments, but you can easily add a single argument overload to handle that case.