在C ++ 11中枚举布尔模板参数

时间:2017-12-04 16:27:01

标签: c++ c++11 templates variadic-templates template-meta-programming

我试图确定如何接受布尔模板参数包

template<bool...B> struct BoolPack{};

并从中构造一个整数参数包,其中的值表示trueBoolPack元素的枚举。枚举可以包含如下:

template<size_t...I> struct IntegerSequence{};

该功能应该包含BoolPack<B...>并返回IntegerSequence<I...>;

template<bool...B>
constexpr auto enumerate( const BoolPack<B...>& b ) {
  // Combination of cumulative sum and mask?
  return IntegerSequence<???>();
}

例如,如果输入为

BoolPack<true,false,true,false,true> b;

该功能应该返回

IntegerSequence<1,0,2,0,3> e();

我对如何实现这一点的最佳猜测是计算部分和 第k个模板参数是

的位置
K = K-1 + static_cast<size_t>(get<K>(make_tuple<B...>));

我不确定这是不是这样做的。难道没有一种更直接的方法不需要制作元组吗?应用递归应该导致

IntegerSequence<1,1,2,2,3> s();

然后将此分量乘以原始BoolPack的元素。这是要走的路,还是我可以在没有元组的情况下做到这一点?

谢谢!

2 个答案:

答案 0 :(得分:4)

您的实施方式

template<bool...B>
constexpr auto enumerate( const BoolPack<B...>& b ) {
  // Combination of cumulative sum and mask?
  return IntegerSequence<???>();
}

无法工作,因为C ++是一种强类型语言,因此返回的类型不能取决于从编译时可以执行的代码中获得的总和。

我知道这些值在编译时是已知的,因此值可以在编译时计算;但是我看到的最佳方式是通过特定的类型特征。

通过示例(抱歉:将IntegerSequence重命名为IndexSequence,使其更接近C ++ 14 std::index_sequence

template <std::size_t, typename, std::size_t ...>
struct BoolIndexer
 { };

template <std::size_t N, bool ... Bs, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<true, Bs...>, Is...>
   : public BoolIndexer<N+1U, BoolPack<Bs...>, Is..., N>
 { };

template <std::size_t N, bool ... Bs, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<false, Bs...>, Is...>
   : public BoolIndexer<N, BoolPack<Bs...>, Is..., 0U>
 { };

template <std::size_t N, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<>, Is...>
 { using type = IndexSequence<Is...>; };

以下是完整的编译示例

#include <iostream>
#include <type_traits>

template <bool ... B>
struct BoolPack
 { };

template<size_t...I>
struct IndexSequence
 { };

template <std::size_t, typename, std::size_t ...>
struct BoolIndexer
 { };

template <std::size_t N, bool ... Bs, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<true, Bs...>, Is...>
   : public BoolIndexer<N+1U, BoolPack<Bs...>, Is..., N>
 { };

template <std::size_t N, bool ... Bs, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<false, Bs...>, Is...>
   : public BoolIndexer<N, BoolPack<Bs...>, Is..., 0U>
 { };

template <std::size_t N, std::size_t ... Is>
struct BoolIndexer<N, BoolPack<>, Is...>
 { using type = IndexSequence<Is...>; };



int main ()
 {
   using type1 = BoolPack<true,false,true,false,true>;
   using type2 = typename BoolIndexer<1U, type1>::type;

   static_assert( std::is_same<type2, IndexSequence<1,0,2,0,3>>{}, "!" );
 }

如果你真的需要一个函数来获得转换,使用BoolIndexer类型特征你可以简单地写如下

template <bool ... Bs>
constexpr auto getIndexSequence (BoolPack<Bs...> const &)
   -> typename BoolIndexer<1U, BoolPack<Bs...>>::type
 { return {}; }

并以这种方式调用

auto is = getIndexSequence(BoolPack<true,false,true,false,true>{});

答案 1 :(得分:1)

另一种可能的解决方案是通过创建constexpr函数

template <bool ... Bs>
constexpr std::size_t getNumTrue (BoolPack<Bs...> const &, std::size_t top)
 {
   using unused = int[];

   std::size_t  cnt = -1;
   std::size_t  ret { 0 };

   (void)unused { 0, (++cnt <= top ? ret += Bs : ret, 0)... };

   return ret;
 }
可以调用

来设置IndexSequence的模板值;不幸的是,下面的示例使用std::make_index_sequencestd::index_sequence作为C ++ 14(从...开始)功能

template <bool ... Bs, std::size_t ... Is>
constexpr auto gisH (BoolPack<Bs...> const &,
                     std::index_sequence<Is...> const &)
   -> IndexSequence<(Bs ? getNumTrue(BoolPack<Bs...>{}, Is) : 0U)...>
 { return {}; }

template <bool ... Bs>
constexpr auto getIndexSequence (BoolPack<Bs...> const & bp)
 { return gisH(bp, std::make_index_sequence<sizeof...(Bs)>{}); }

以下是完整的编译示例

#include <utility>
#include <iostream>
#include <type_traits>

template <bool ... B>
struct BoolPack
 { };

template <std::size_t...I>
struct IndexSequence
 { };

template <bool ... Bs>
constexpr std::size_t getNumTrue (BoolPack<Bs...> const &, std::size_t top)
 {
   using unused = int[];

   std::size_t  cnt = -1;
   std::size_t  ret { 0 };

   (void)unused { 0, (++cnt <= top ? ret += Bs : ret, 0)... };

   return ret;
 }

template <bool ... Bs, std::size_t ... Is>
constexpr auto gisH (BoolPack<Bs...> const &,
                     std::index_sequence<Is...> const &)
   -> IndexSequence<(Bs ? getNumTrue(BoolPack<Bs...>{}, Is) : 0U)...>
 { return {}; }

template <bool ... Bs>
constexpr auto getIndexSequence (BoolPack<Bs...> const & bp)
 { return gisH(bp, std::make_index_sequence<sizeof...(Bs)>{}); }

int main()
 {
   using typeBP = BoolPack<true,false,true,false,true>;

   auto is = getIndexSequence(typeBP{});

   static_assert( std::is_same<decltype(is),
                               IndexSequence<1,0,2,0,3>>{}, "!" );
 }