使用布尔值设置位集的最佳方法
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;
}
假设我有 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;
}