在C++中,对于“bool”值使用位运算符&、|和^是否有任何不合适的原因?
有时我遇到想要恰好满足两个条件之一(异或)的情况,所以我会将^操作符放入条件表达式中。我有时也希望所有条件都被评估,无论结果是真还是假(而不是短路),所以我使用&和|。有时我需要累加布尔值,这时&=和|=就非常有用。
当我这样做时,可能会引起一些人的反感,但代码仍然意义明确且更加简洁。是否有任何不应该对布尔值使用这些运算符的原因?是否有任何现代编译器会产生错误的结果?
在C++中,对于“bool”值使用位运算符&、|和^是否有任何不合适的原因?
有时我遇到想要恰好满足两个条件之一(异或)的情况,所以我会将^操作符放入条件表达式中。我有时也希望所有条件都被评估,无论结果是真还是假(而不是短路),所以我使用&和|。有时我需要累加布尔值,这时&=和|=就非常有用。
当我这样做时,可能会引起一些人的反感,但代码仍然意义明确且更加简洁。是否有任何不应该对布尔值使用这些运算符的原因?是否有任何现代编译器会产生错误的结果?
||
和&&
是布尔运算符,内置的运算符保证只会返回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
,然后完成即可。
&=
和|=
累加的说明就更好了。在阅读整个线程并进行测试后,我检查并了解到gcc在bool passed_tests_ = true; passed_tests_ &= 8;
之后会返回false,这意味着我的某个测试对后续编辑很脆弱...(寻找这个问题是我来到这里的原因) - sage两个主要原因。简而言之,请仔细考虑;可能有一个很好的理由,但如果有的话,请在您的注释中非常明确,因为它可能很脆弱,并且正如您自己所说,人们通常不习惯看到这样的代码。
首先,如果您对除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首先表达了这个概念。
其次,作为按位运算符,|
、&
和^
不会短路。此外,即使使用明确的括号,单个语句中链接多个按位运算符也可以被优化编译器重新排序,因为这三个操作通常是可交换的。如果操作的顺序很重要,这一点非常重要。
换句话说
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
是你想要的。
!==
),所以如果你要计算一系列bool
值的异或,你需要在循环体中写acc = acc!= condition(i);
。编译器可能可以像存在!==
一样高效地处理这个问题,但有些人可能觉得它看起来不太好,更喜欢将布尔值作为整数相加,然后测试和是否为奇数。 - Marc van Leeuwen您提出了问题:
“在C ++中,对于“bool”值,使用按位运算符&,|和^是否有任何理由不使用?”
是的,逻辑运算符即内置的高级布尔运算符!, 即!
,&&
和||
,具有以下优点:
保证将参数转换为bool
,即0
和1
的序数值。
保证短路评估,其中表达式的评估在知道最终结果后立即停止。
这可以解释为一个三值逻辑,包括真、假和不确定。
可读性更好的文本等效项not
,and
和or
,即使我自己不使用它们。
正如评论者Antimony在评论中指出的那样,位级别运算符也有替代标记,即bitand
,bitor
,xor
和compl
,但我认为这些标记不如and
,or
和not
易读。
简而言之,高级运算符的每个优点都是位级别运算符的缺点。
特别地,由于按位运算符缺乏将参数转换为0/1的功能,因此例如1 & 2
→ 0
,而1 && 2
→ true
。另外,按位异或^
也可能出现问题。将1和2视为布尔值时,它们是相同的,即true
,但将它们视为位模式时,它们是不同的。
您提供了一些背景信息:
“有时我遇到情况,我只想要两个条件中的一个为真(XOR),所以我只需将^运算符放入条件表达式中。”
好吧,位运算符的优先级更高,比逻辑运算符更高。这意味着在混合表达式中,例如
a && b ^ c
你会得到一个也许出乎意料的结果 a && (b ^ c)
。
相反,只需写
(a && b) != c
表达更简洁地表达您的意思。a ^ b ^ c
,那么这不是一个表达式,表示“a
、b
或 c
中的任意一个为真”。相反,它表示“a
、b
和 c
中奇数个为真”,这可能是其中一个或全部 3 个…a
、b
和 c
的 either/or,只需编写(a + b + c) == 1
或者,对于非bool
类型的参数,将它们转换为bool
:
(!!a + !!b + !!c) == 1
&=
累积布尔结果。你进一步解释说:
“有时我也需要累积布尔值,
&=
和|=?
可以非常有用。”
好的,这相当于检查是否分别满足所有条件或任何条件,并且德摩根定律告诉您如何从一个转换为另一个。也就是说,您只需要其中之一。您原则上可以使用 *=
作为 &&=
运算符(因为像老乔治·布尔一样发现,逻辑 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
结果不同的原因是bitlevel &=
没有将其右侧参数转换为 bool
。
那么,您需要使用&=
的哪个结果?
如果是前者true
,则最好定义一个运算符(如上所述)或命名函数,或使用右侧表达式的显式转换,或完整地编写更新。
bitand
、bitor
、xor
和compl
。我想这就是为什么我使用了“可读性”这个限定词。当然,例如compl 42
的可读性是主观的。;-) - Cheers and hth. - Alfbool
的保证”只是约定的结果,而不是C++语言实际给出的保证。 - Marc van LeeuwenGetMessage()
函数,它返回三态BOOL:非零,0或-1。^^
运算符仍然是有用的。它将 a) 在布尔环境中评估操作数,并且 b) 保证返回 1 或 0。 - Craig McQueeninline bool XOR(bool a,bool b) { return a!=b; }
就可以得到你想要的结果,但它是一个函数而不是(中缀)运算符。或者你可以直接使用 !=
或重载其他具有相同含义的运算符,但当然你需要非常小心,以免意外地使用同名的意外重载。顺便说一下,||
和 &&
返回的是 true
或 false
,而不是 1
或 0
。 - Marc van Leeuwen!
、||
和 &&
在布尔上下文中对其参数进行求值的原因仅仅是因为这些运算符很少被重载以接受其他类型;据我所知,该语言确实允许这样的重载,因此不保证在布尔上下文中对参数进行求值。 - Marc van Leeuwenbool onlyAIsTrue = (a && !b); // you could use bitwise XOR here
bool onlyBIsTrue = (b && !a); // and not need this second line
if (onlyAIsTrue || onlyBIsTrue)
{
.. stuff ..
}
编辑:虽然你没有明确表示你想要“if”语句的条件(尽管这似乎最有可能),但我的建议仍然是使用一个中间布尔值。