使用布尔值设置位集的最佳方法

best way to set a bitset with boolean values

假设我有 3 个 bool 类型值

bool canwalk=true;
bool cantalk=false;
bool caneat=false;

我想设置一个bitset表示三个

std::bitset<3> foo;

如何使用布尔值构建 bitset? 我想做这样的事情

 std::bitset<3> foo(canWalk,cantalk,caneat); //giving me 100

引入一个新的 api,它可以为您提供 bitset 在参数中接受的字符串输入。

为了更通用,建议使用 bool 数组或 [std::vector<bool>][1] 来摆脱 getString()

中的这些变量参数
inline std::string getString(bool canwalk, bool canTalk, bool canEat)
{
std::stringstream input;
str << canwalk?1:0 << cantalk?1:0 << caneat?1:0;
return input.str();
}

现在可以将 bitset 定义为:

std::bitset<3> foo (getString(canwalk, canTalk, canEat));

您基本上需要一个构建器,它将从您的布尔集构建一个初始值以传递给 std::bitset 的构造函数。您可以通过可变参数模板在编译时(而不是运行时)执行此操作,如下所示:

template <unsigned long long initialValue> 
constexpr unsigned long long bitset_value_builder_impl() { return initialValue; }

template <unsigned long long initialValue, typename First, typename ... Args>
constexpr unsigned long long bitset_value_builder_impl(First &&first, Args &&...args) {
    return first ? 
        bitset_value_builder_impl< (initialValue | (1UL<<sizeof...(args)) ), Args...>(std::forward<Args>(args)...) :
        bitset_value_builder_impl< (initialValue & ~(1UL<<sizeof...(args)) ), Args...>(std::forward<Args>(args)...);
}

template <typename First, typename ... Args>
constexpr unsigned long long bitset_value_builder(First &&first, Args &&...args) {   
    return bitset_value_builder_impl<0, First, Args...>(std::forward<First>(first), std::forward<Args>(args)...);
}

int main()
{
    bool canwalk=true;
    bool cantalk=false;
    bool caneat=false;    
    std::bitset<3> bits{bitset_value_builder(canwalk, cantalk, caneat)};
    std::cout << bits << std::endl; //100
}

以 Shivendra Agarwal 为例,但使用接收 unsigned long long 的构造函数,我提出以下可变参数模板函数(更通用)

template <typename ... Args>
unsigned long long getULL (Args ... as)
 {
   using unused = int[];

   unsigned long long ret { 0ULL };

   (void) unused { 0, (ret <<= 1, ret |= (as ? 1ULL : 0ULL), 0)... };

   return ret;
 }

允许 foo 的初始化如下

std::bitset<3> foo{ getULL(canwalk, cantalk, caneat) };

这仅在 std::bitset 的维度不大于 unsigned long long 中的位数时有效(使用 3 肯定是安全的)。

以下是完整的工作示例

#include <bitset>
#include <iostream>

template <typename ... Args>
unsigned long long getULL (Args ... as)
 {
   using unused = int[];

   unsigned long long ret { 0ULL };

   (void) unused { 0, (ret <<= 1, ret |= (as ? 1ULL : 0ULL), 0)... };

   return ret;
 }

int main()
 {
   bool canwalk=true;
   bool cantalk=false;
   bool caneat=false;

   std::bitset<3> foo{ getULL(canwalk, cantalk, caneat) };

   std::cout << foo << std::endl;
 }

恕我直言,

类型的初始化
std::bitset<3> foo(canWalk, cantalk, caneat);

是危险的(容易出错),因为要求 std::bitset(示例中的 3)的模板参数对应于初始化的参数数量。

我建议创建一个 "make" 函数(遵循 std::pair()std::tuple()std::make_unique()std::make_shared 的综合示例),其中类型并且参数的数量固定了 returned 类型。

所以我提出以下 makeBitSet() 函数,其中 return 一个 std::bitset<N> 其中 N 是参数的数量

template <typename ... Args>
std::bitset<sizeof...(Args)> makeBitSet (Args ... as)
 {
   using unused = bool[];

   std::bitset<sizeof...(Args)>  ret;

   std::size_t ui { ret.size() };

   (void) unused { true, (ret.set(--ui, as), true)... };

   return ret;
 }

函数可以如下使用

std::bitset<3> foo{ makeBitSet(canwalk, cantalk, caneat) };

而且(更好,恕我直言),使用 C++11 auto

auto foo = makeBitSet(canwalk, cantalk, caneat);

观察到,从 C++14 开始,makeBitSet() 可以使用 returning auto 类型

template <typename ... Args>
auto makeBitSet (Args ... as)
 {
   // ...

避免烦人的 std::bitset<sizeof...(Args)> 冗余。

此外,从 C++17 开始,您可以使用模板折叠,并丢弃 unused 数组(以及相应的 using 声明),makeBitSet() 可以是简化为 [编辑:修改,以提高性能,遵循 Mooing Duck 的建议(谢谢!)]

template <typename ... Args>
auto makeBitSet (Args ... as)
 {
   std::bitset<sizeof...(Args)>  ret;

   std::size_t ui { ret.size() };

   ( ret.set(--ui, as), ... );

   return ret;
 }

以下是完整的 C++11 示例

#include <bitset>
#include <iostream>

template <typename ... Args>
std::bitset<sizeof...(Args)> makeBitSet (Args ... as)
 {
   using unused = bool[];

   std::bitset<sizeof...(Args)>  ret;

   std::size_t ui { ret.size() };

   (void) unused { true, (ret.set(--ui, as), true)... };

   return ret;
 }

int main()
 {
   bool canwalk { true  };
   bool cantalk { false };
   bool caneat  { false };

   auto foo = makeBitSet(canwalk, cantalk, caneat);

   std::cout << foo << std::endl;
 }