如何将函数静态应用于非类型模板包的各个元素并对结果求和

How to statically apply a function to individual elements of a non-type template pack and sum the results?

本文关键字:元素 求和 结果 函数 静态 应用于 类型      更新时间:2023-10-16

有没有更好的方法更紧凑地在编译时实现下面的nBits的计算?请注意,问题不在于如何实施n_active_bits,我知道该怎么做。

constexpr int n_active_bits(int m) { /* count the bits */ }
template <uint8_t...Masks>
struct MaskPack
{
    // is there a more concise way than to implement 
    //the auxiliary recursive function count_bits?
    static constexpr uint8_t nBits = count_bits(Masks...);
private:
    template <typename M, typename...Ms>
    static constexpr uint8_t count_bits(M&& m, Ms&&...ms)
    {
            return n_active_bits(m) + count_bits(ms...);
    }
    static constexpr uint8_t count_bits()
    {
            return 0;
    }
};

我尝试使用单行C++17折,但没有运气:

static constexpr uint8_t nBits = (n_active_bits(Masks) + ...);

使用折叠,您可以按如下方式进行操作:

template <uint8_t...Masks>
struct MaskPack {
   static constexpr uint8_t nBits = (n_active_bits(Masks) + ...);
};

现场演示

请注意,n_active_bits必须constexpr

constexpr int n_active_bits(unsigned int mask) {
  return mask == 0 ? 0 : (mask & 0x01) + n_active_bits(mask >> 1);
}

它需要一些样板。

template<size_t I, class Indexes>
struct offset_indexes;
template<size_t I, class Indexes>
using offset_indexes_t = typename offset_indexes<I,Indexes>::type;
template<size_t I, size_t...Is>
struct offset_indexes<I, std::index_sequence<Is...>> {
  using type=std::index_sequence<(I+Is)...>;
};

然后是二进制折叠:

template<size_t...Is, class Tuple>
constexpr auto retuple( std::index_sequence<Is...>, Tuple&& tuple ) {
  return std::forward_as_tuple( std::get<Is>(std::forward<Tuple>(tuple))... );
}
template<class F, class T>
constexpr T binary_fold( F&& f, std::tuple<T>&& tuple ) {
  return std::get<0>(std::move(tuple));
}
template<class Tuple>
struct first_element {};
template<class Tuple>
using first_element_t=typename first_element<Tuple>::type;
template<template<class...>class Z, class T0, class...Ts>
struct first_element<Z<T0,Ts...>>{using type=T0;};
template<class F, class Tuple, class E=first_element_t<std::decay_t<Tuple>>>
constexpr std::decay_t<E> binary_fold( F&& f, Tuple&& tuple ) {
  constexpr auto count = std::tuple_size<std::decay_t<Tuple>>{};
  using first_half = std::make_index_sequence< count/2 >;
  using second_half = offset_indexes_t<
    count/2,
    std::make_index_sequence< (count+1)/2 >
  >;
  return f(
    binary_fold( f, retuple( first_half{},  std::forward<Tuple>(tuple) ) ),
    binary_fold( f, retuple( second_half{}, std::forward<Tuple>(tuple) ) )
  );
}

它应该对binary_fold进行递归调用的二叉树。

所有这些都使count_bits变得容易:

template <class...Ms>
constexpr size_t count_bits(Ms&&...ms)
{
  return binary_fold(
    std::plus<>{},
    std::make_tuple(n_active_bits(std::forward<Ms>(ms))...)
  );
}

但是请注意,有很多样板文件。

我做了一个更复杂的二进制折叠,因为左/右折叠具有很深的递归深度。

活生生的例子。