C ++模板化包,折叠两次

C++ templated packs, fold twice

我已经阅读了一些类似的问题,但我没有找到我正在寻找的确切内容。

以纯数学方式,列表被递归定义为:(head, rest).

其中 head 是列表中的第一个元素,rest 是一个列表。 因此,例如 (1,2,3,4) 表示为 (1, (2,(3,(4,[])))) 其中 [] 是空列表。

然后如果我们想遍历列表,我们可以写一个递归函数或多或少像这样:

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

如果我们想迭代每 2 个元素,我们会这样做:

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

我正试图在 C++ 中获得第二种行为。

在 C++ 17 中,引入了折叠,因此可以这样做:

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

但是假设我们想要相邻参数的乘积之和,例如 sum(1,2,3,4)1*2 + 3*4.

在这种情况下,我们需要"fold twice"让2个头执行操作并传递列表的其余部分。类似于我的伪代码。

有没有人有关于如何连续获得 2 折的建议?

编辑: 我特别想用折叠来做,即在函数声明中,而不必依赖递归模板函数。

你可以一次解压2个参数,像这样:

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

并提供无参数的基本情况重载:

auto sum_products() { return 0; }

然后像这样使用它:

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

这里是 demo

请注意,这仅适用于偶数个参数,但您可以轻松添加单个参数重载来处理这种情况。

更简单的方法是将原型更改为成对:

template <typename ...Pairs>
auto sum_products(Pairs ...ts)
{
    return (... + (ts.first * ts.second));
}

否则通用解决方案是使用 index_sequence:

template <std::size_t... Is, typename T>
auto sum_products_impl(std::index_sequence<Is...>, const T& t)
{
    return (... + (std::get<2 * Is>(t) * std::get<2 * Is + 1>(t)));
}

template <typename ...Ts>
auto sum_products(Ts ...ts)
{
    static_assert(sizeof...(Ts) % 2 == 0);
    return sum_products_impl(std::make_index_sequence<sizeof...(Ts) / 2>(), std::tie(ts...));
}