在 C++ 中使用布尔值按位运算符
-
09-06-2019 - |
题
在 C++ 中是否有任何理由不使用按位运算符 &、| 和 ^ 来表示“bool”值?
有时我会遇到这样的情况:我希望两个条件之一恰好为真(XOR),所以我只是将 ^ 运算符放入条件表达式中。有时我还希望对条件的所有部分进行评估,无论结果是否为真(而不是短路),因此我使用 & 和 |。有时我还需要积累布尔值,&=和|=可能非常有用。
当我这样做时,有些人感到惊讶,但代码仍然比其他方式更有意义和更清晰。有什么理由不将它们用于布尔值吗?是否有任何现代编译器会给出不好的结果?
解决方案
||
和 &&
是布尔运算符,内置运算符保证返回 true
或者 false
. 。没有其他的。
|
, &
和 ^
是按位运算符。当您操作的数字域只是 1 和 0 时,它们是完全相同的,但如果您的布尔值不严格是 1 和 0(如 C 语言的情况),您可能会出现一些行为你不想要。例如:
BOOL two = 2;
BOOL one = 1;
BOOL and = two & one; //and = 0
BOOL cand = two && one; //cand = 1
然而,在 C++ 中, bool
类型保证只能是 true
或一个 false
(分别隐式转换为 1
和 0
),因此从这种立场来看,不必担心,但人们不习惯在代码中看到此类内容,这一事实为不这样做提供了一个很好的理由。说啊 b = b && x
并完成它。
其他提示
两个主要原因。总之,慎重考虑;可能有一个很好的理由,但是如果您的评论非常明确,因为它可能很脆弱,而且正如您自己所说,人们通常不习惯看到这样的代码。
按位异或 != 逻辑异或(0 和 1 除外)
首先,如果你的操作价值观不是 false
和 true
(或者 0
和 1
, ,作为整数), ^
运算符可以引入与逻辑异或不同的行为。例如:
int one = 1;
int two = 2;
// bitwise xor
if (one ^ two)
{
// executes because expression = 3 and any non-zero integer evaluates to true
}
// logical xor; more correctly would be coded as
// if (bool(one) != bool(two))
// but spelled out to be explicit in the context of the problem
if ((one && !two) || (!one && two))
{
// does not execute b/c expression = ((true && false) || (false && true))
// which evaluates to false
}
感谢用户@Patrick 首先表达了这一点。
操作顺序
第二, |
, &
, , 和 ^
, ,作为按位运算符,不要短路。此外,在单个语句中链接在一起的多个按位运算符(即使使用显式括号)也可以通过优化编译器重新排序,因为所有 3 个操作通常都是可交换的。如果操作顺序很重要,这一点很重要。
换句话说
bool result = true;
result = result && a() && b();
// will not call a() if result false, will not call b() if result or a() false
并不总是给出相同的结果(或最终状态)
bool result = true;
result &= (a() & b());
// a() and b() both will be called, but not necessarily in that order in an
// optimizing compiler
这一点尤其重要,因为你可能无法控制方法 a()
和 b()
, ,或者其他人可能会在不理解依赖关系的情况下出现并在稍后更改它们,并导致令人讨厌的(通常仅限于发布构建)错误。
我认为
a != b
就是你想要的
扬起的眉毛应该足以告诉你停止这样做。您不是为编译器编写代码,而是首先为其他程序员编写代码,然后再为编译器编写代码。即使编译器可以工作,让其他人感到惊讶也不是您想要的 - 位运算符适用于位运算而不是布尔运算。
我想你也用叉子吃苹果吧?它确实有效,但会让人们感到惊讶,所以最好不要这样做。
位级运算符的缺点。
你问:
“有什么理由不使用按位运算符
&
,|
, , 和^
C++ 中的“bool”值?”
是的 逻辑运算符, ,即内置的高级布尔运算符 !
, &&
和 ||
, ,具有以下优点:
有保证 参数转换 到
bool
, , IE。到0
和1
顺序值。有保证 短路评估 一旦知道最终结果,表达式计算就会停止。
这可以解释为树值逻辑,其中 真的, 错误的 和 不定.可读的等效文本
not
,and
和or
, ,即使我自己不使用它们。
正如读者 Antimony 在评论中指出的那样,位级运算符也有替代标记,即bitand
,bitor
,xor
和compl
, ,但在我看来,这些的可读性比and
,or
和not
.
简而言之,高级运算符的每一个优点都是位级运算符的缺点。
特别是,由于按位运算符缺乏到 0/1 的参数转换,因此您会得到例如 1 & 2
→ 0
, , 尽管 1 && 2
→ true
. 。还 ^
, ,按位异或,可能会以这种方式出现错误。视为布尔值1和2相同,即 true
, ,但被视为位模式,它们是不同的。
如何表达逻辑性 两者任一 在C++中。
然后你为这个问题提供一些背景知识,
“有时我会遇到这样的情况:我希望两个条件之一恰好为真 (XOR),所以我只是将 ^ 运算符放入条件表达式中。”
那么,按位运算符有 更高的优先级 比逻辑运算符。这尤其意味着在混合表达中,例如
a && b ^ c
你会得到也许意想不到的结果 a && (b ^ c)
.
相反只写
(a && b) != c
更简洁地表达您的意思。
对于多个参数 两者任一 没有 C++ 运算符可以完成这项工作。例如,如果你写 a ^ b ^ c
这并不是一个表达“要么 a
, b
或者 c
是真的”。相反,它说:“奇数个 a
, b
和 c
是真的”,可能是其中 1 个或全部 3 个……
表达一般的“非此即彼” a
, b
和 c
属于类型 bool
, , 写吧
(a + b + c) == 1
或者,与非bool
参数,将它们转换为 bool
:
(!!a + !!b + !!c) == 1
使用 &=
累积布尔结果。
你进一步详细说明,
“有时我也需要积累布尔值,并且
&=
和|=?
可能非常有用。”
嗯,这对应于检查是否分别 全部 或者 任何 条件满足,并且 德摩根定律 告诉您如何从一种转到另一种。IE。你只需要其中之一。原则上你可以使用 *=
作为一个 &&=
- 运算符(正如老乔治布尔发现的那样,逻辑 AND 可以很容易地表达为乘法),但我认为这会让代码的维护者感到困惑,甚至可能会误导。
还请考虑:
struct Bool
{
bool value;
void operator&=( bool const v ) { value = value && v; }
operator bool() const { return value; }
};
#include <iostream>
int main()
{
using namespace std;
Bool a = {true};
a &= true || false;
a &= 1234;
cout << boolalpha << a << endl;
bool b = {true};
b &= true || false;
b &= 1234;
cout << boolalpha << b << endl;
}
使用 Visual C++ 11.0 和 g++ 4.7.1 的输出:
true false
结果差异的原因是位级别 &=
不提供转换为 bool
其右侧论证。
那么,您希望使用这些结果中的哪一个 &=
?
如果是前者, true
, ,然后更好地定义一个运算符(例如如上所述)或命名函数,或使用右侧表达式的显式转换,或完整编写更新。
与 Patrick 的回答相反,C++ 没有 ^^
用于执行短路异或的运算符。如果你想一想,有一个 ^^
无论如何,运算符没有意义:对于异或,结果始终取决于两个操作数。然而,帕特里克警告非bool
“布尔”类型在比较时同样适用 1 & 2
到 1 && 2
. 。Windows 就是一个典型的例子 GetMessage()
函数,返回一个三态 BOOL
: :非零, 0
, , 或者 -1
.
使用 &
代替 &&
和 |
代替 ||
这不是一个罕见的错字,所以如果你是故意这样做的,值得评论说明原因。
帕特里克提出了很好的观点,我不会重复。但是,我可能会建议通过使用命名良好的布尔变量,尽可能将“if”语句减少为可读的英语。例如,这是使用布尔运算符,但您同样可以使用按位并适当地命名布尔值:
bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here
bool onlyBIsTrue = (b && !a); // and not need this second line
if (onlyAIsTrue || onlyBIsTrue)
{
.. stuff ..
}
您可能认为使用布尔值似乎没有必要,但它有两个主要帮助:
- 您的代码更容易理解,因为“if”条件的中间布尔值使条件的意图更加明确。
- 如果您使用非标准或意外的代码,例如布尔值的按位运算符,人们可以更容易地理解您这样做的原因。
编辑:您没有明确表示您想要“if”语句的条件(尽管这似乎最有可能),这是我的假设。但我对中间布尔值的建议仍然有效。
IIRC,许多 C++ 编译器在尝试将按位运算的结果转换为 bool 时会发出警告。您必须使用类型转换才能使编译器满意。
在 if 表达式中使用按位运算也会受到同样的批评,尽管编译器可能不会这样做。任何非零值都被视为 true,因此“if (7 & 3)”之类的内容将为 true。这种行为在 Perl 中可能是可以接受的,但 C/C++ 是非常明确的语言。我认为斯波克眉毛是尽职的。:) 我会附加“== 0”或“!= 0”以使其完全清楚您的目标是什么。
但无论如何,这听起来像是个人喜好。我会通过 lint 或类似工具运行代码,看看它是否也认为这是一个不明智的策略。就我个人而言,它读起来像是一个编码错误。
对 bool 使用按位运算有助于节省处理器不必要的分支预测逻辑,这是由逻辑运算引入的“cmp”指令产生的。
用按位运算(所有操作数均为布尔值)替换逻辑会生成更高效的代码,并提供相同的结果。理想情况下,效率应该超过使用逻辑运算进行排序时可以利用的所有短路优势。
这可能会使代码有点难以阅读,尽管程序员应该对其进行注释并说明这样做的原因。