提问人:Zebrafish 提问时间:8/18/2019 最后编辑:SomeRandomDevZebrafish 更新时间:8/23/2019 访问量:3174
我可以一次检查一小群布尔值吗?
Can I check a small array of bools in one go?
问:
这里有一个类似的问题,但该问题中的用户似乎有一个更大的数组或向量。如果我有:
bool boolArray[4];
我想检查是否所有元素都是假的,我可以分别检查 [ 0 ], [ 1 ] , [ 2 ] 和 [ 3 ],或者我可以遍历它。由于(据我所知)false 的值应该为 0,而 0 以外的任何值都是 true,因此我想简单地做:
if ( *(int*) boolArray) { }
这有效,但我意识到它依赖于 bool 是 1 个字节,int 是 4 个字节。如果我强制转换为 (std::uint32_t) 可以吗,还是仍然是一个坏主意?我只是碰巧在一个数组中有 3 或 4 个布尔值,想知道这是否安全,如果不是,是否有更好的方法可以做到这一点。
另外,如果我最终得到超过 4 个布尔值但少于 8 个布尔值,我可以用 std::uint64_t 或无符号长多头或其他东西做同样的事情吗?
答:
正如 πάντα ῥεῖ 在评论中指出的那样,std::bitset
可能是以无 UB 方式处理该问题的最佳方法。
std::bitset<4> boolArray {};
if(boolArray.any()) {
//do the thing
}
如果你想坚持使用数组,你可以使用 std
::any_of,但这需要(可能是读者特有的)使用 functor,它只返回它的参数:
bool boolArray[4];
if(std::any_of(std::begin(boolArray), std::end(boolArray), [](bool b){return b;}) {
//do the thing
}
类型双关语 4 秒可能是一个坏主意 - 您无法确定每种类型的大小。它可能适用于大多数架构,但可以保证在任何情况下都能在任何地方工作。bool
int
std::bitset
评论
myBitset[3]
myInt & (1 << 3)
std::bitset
标准库以 std::all_of、std::any_of、std::none_of 算法的形式提供您需要的内容。
您可以使用 std::bitset<N>::any
:
如果任何位设置为 ,则返回 ,否则 。true
true
false
#include <iostream>
#include <bitset>
int main ()
{
std::bitset<4> foo;
// modify foo here
if (foo.any())
std::cout << foo << " has " << foo.count() << " bits set.\n";
else
std::cout << foo << " has no bits set.\n";
return 0;
}
如果要返回所有位或不将所有位设置为 on,则可以分别使用 或。true
std::bitset<N>::all
std::bitset<N>::none
...对于强制性的“滚动你自己的”答案,我们可以为任何数组提供一个简单的类似“或”的函数,如下所示:bool[N]
template<size_t N>
constexpr bool or_all(const bool (&bs)[N]) {
for (bool b : bs) {
if (b) { return b; }
}
return false;
}
或者更简洁地说,
template<size_t N>
constexpr bool or_all(const bool (&bs)[N]) {
for (bool b : bs) { if (b) { return b; } }
return false;
}
这也具有短路(如 )和在编译时可计算时完全优化的优点。||
除此之外,如果你想检查类型双关语的原始想法到其他类型以简化观察,我非常建议你不要这样做,而是把它看作是 .这将允许您提供一个简单的表示查看器,该查看器可以自动缩放到查看对象的实际大小,允许迭代其各个字节,并允许您更轻松地确定表示是否与特定值匹配(例如,零或非零)。我不完全确定这样的检查是否会调用任何 UB,但我可以肯定地说,任何此类类型的构造都不可能是可行的常量表达式,因为需要重新解释转换为 or 或类似(显式或 in),因此不能轻易优化。bool[N]
char[N2]
N2 == (sizeof(bool) * N)
char*
unsigned char*
std::memcpy()
评论
std::begin()
std::end()
有几个答案已经解释了好的替代方案,特别是 和 .我单独写信是为了指出,除非你知道我们不知道的东西,否则以这种方式在 和 之间输入双关语是不安全的,原因如下:std::bitset
std::any_of()
bool
int
int
正如多个答案所指出的那样,可能不是四个字节。- M.M在评论中指出,可能不是一个字节。我不知道任何现实世界的架构曾经出现过这种情况,但它仍然是规范合法的。它(可能)不能小于一个字节,除非编译器正在用它的内存模型做一些非常复杂的隐藏球的诡计,而多字节布尔值似乎相当无用。但请注意,字节首先不必是 8 位。
bool
int
可以有陷阱表示。也就是说,某些位模式在被强制转换为 时会导致未定义的行为是合法的。这在现代架构中很少见,但可能会出现在(例如)ia64 或任何带有符号零的系统上。int
- 无论您是否需要担心上述任何一项,您的代码都违反了严格的别名规则,因此编译器可以在假设 bools 和 int 是完全独立的对象且生存期不重叠的情况下自由地“优化”它。例如,编译器可能会确定初始化布尔数组的代码是死存储并消除它,因为在取消引用指针之前的某个时间点,布尔“必须”不复存在*。还可能出现与寄存器重用和加载/存储重新排序相关的更复杂的情况。C++ 标准明确允许所有这些不合格行为,该标准表示,当您进行这种类型的双关语时,行为是未定义的。
您应该使用其他答案提供的替代解决方案之一。
* 通过将内存转换为 int 并存储一个整数来重用指向的内存是合法的(有一些限制,特别是在对齐方面),尽管如果你真的想这样做,如果你想稍后读取生成的 int,则必须通过 std::launder
。无论如何,编译器有权在看到读取后假定您已经执行了此操作,即使您没有调用 launder。boolArray
boolArray
评论
std::bitset
enum class MouseButtonDown: std::uint8_t { Left = 1, Middle = 2, Right = 4 }