在C ++中使用按位运算符来进行布尔运算
是否有任何理由不使用C ++中的“bool”值按位运算符&,|和^?
我有时遇到两种条件中的一种是真的(XOR),所以我只是把^运算符放到一个条件expression式中。 我也有时候希望评估一个条件的所有部分,不pipe结果是否正确(而不是短路),所以我使用&和|。 我也需要积累布尔值,&=和| =可以是非常有用的。
这样做的时候,我已经提出了一些眉毛,但代码仍然是有意义的,比其他方式更清洁。 有没有任何理由不使用这些bools? 有没有现代编译器给这个不好的结果?
||
和&&
是布尔运算符,并且内build的运算符保证返回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
types保证只是一个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为了expression这一点。
操作顺序
二, |
, &
和^
作为按位运算符,不要短路。 另外,多个按位运算符在一个语句中链接在一起 – 即使使用明确的括号 – 也可以通过优化编译器来重新sorting,因为所有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
是你想要的
抬起的眉毛应该告诉你足够的停止这样做。 你不会为编译器编写代码,而是先为同行编程人员编写代码,然后编译编译器。 即使编译器工作,令人惊讶的其他人不是你想要的 – 按位运算符是不是为bools位操作。
我想你还用叉子吃苹果? 它的作品,但它令人惊讶,所以最好不要这样做。
位级操作员的缺点
你问:
“是否有任何理由不使用按位运算符
&
,|
,^
在C ++中的“布尔”值? ”
是的, 逻辑运算符 ,就是内置的高级布尔运算符!
, &&
和||
,提供以下优点:
-
保证将参数转换为
bool
,即0
和1
序数值。 -
保证短路评估 ,其中expression评估一旦知道最终结果就立即停止。
这可以被解释为具有真 , 假和不确定性的树值逻辑。 -
可读的文本等价物
not
and
,or
,即使我自己不使用它们。
正如读者锑在注释中也注意到,位级操作符有替代的令牌,即bitand
,bitor
,xor
和compl
,但是在我看来,这些操作的可读性低于,or
不可读。
简而言之,高层次运营商的每个这样的优势是位运营商的劣势。
特别是,由于按位运算符没有将参数转换为0/1,所以您可以获得例如1 & 2
→ 0
,而1 && 2
→ true
。 也^
,按位排他或可以以这种方式行事不端。 被视为布尔值1和2是相同的,即true
,但被视为位模式,他们是不同的。
如何用C ++expression逻辑。
然后您提供一些问题的背景,
“有时我会遇到两种条件中的一种是真的(XOR),所以我只是把^运算符放到条件expression式中。
那么,按位运算符比逻辑运算符具有更高的优先级 。 这尤其意味着,在一个混合的expression式如
a && b ^ c
你得到了可能意想不到的结果a && (b ^ c)
。
而只是写
(a && b) != c
更简洁地expression你的意思。
对于多重参数要么/或者没有这个工作的C ++操作符。 例如,如果你写a ^ b ^ c
而不是一个expression式“ a
, b
或c
是真的”。 相反,它说:“一个奇数的a
, b
和c
是真实的,这可能是其中的一个或全部3 …
为了expression一般情况,或者当a
, b
和c
是bool
types的时候,就写下来
(a + b + c) == 1
或者使用非bool
参数,将它们转换为bool
:
(!!a + !!b + !!c) == 1
使用&=
来累积布尔结果。
你进一步阐述,
“我也需要积累布尔值,有时和
&=
和|=?
可以是相当有用的。“
那么,这相当于检查是否满足所有条件或任何条件, 德摩根定律告诉你如何从一个到另一个。 即你只需要其中的一个。 你原则上可以使用*=
作为&&=
-operator(就像George Boole发现的那样,逻辑“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输出:
真正 假
结果不同的原因在于,位级&=
不提供对其右手边参数的bool
的转换。
那么,您希望使用这些结果中的哪一个? &=
?
如果前者是true
,那么更好地定义一个运算符(例如上面)或命名函数,或者使用右侧expression式的显式转换,或者写入更新。
与帕特里克的答案相反,C ++没有^^
操作符来执行短路排他或。 如果你仔细想一下,有一个^^
运算符无论如何不会有意义:独占或,结果总是取决于两个操作数。 然而,当比较1 & 2
到1 && 2
时,Patrick关于非bool
“布尔”types的警告同样适用。 一个典型的例子是Windows GetMessage()
函数,它返回一个三态BOOL
:非零, 0
或-1
。
使用&
代替&&
和|
而不是||
不是一个不寻常的错字,所以如果你故意这样做,它值得评论说明原因。
帕特里克提出了很好的观点,我不打算再重复一遍。 但是,我可以build议通过使用有名的布尔variables,尽可能减less“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”条件的中间布尔值使得条件的意图更加明确。
- 如果您使用的是非标准或意外的代码,例如布尔值上的按位运算符,则人们可以更容易地看出您为什么这样做。
编辑:你没有明确地说,你想'条件'的条件声明(尽pipe这似乎最有可能),这是我的假设。 但是我对中间布尔值的build议仍然存在。
IIRC,许多C ++编译器会在试图将一个按位操作的结果作为一个布尔值时发出警告。 你将不得不使用types转换来使编译器感到高兴。
在ifexpression式中使用按位操作可能会产生同样的批评,但编译器可能不会这样做。 任何非零值被认为是真的,所以像“if(7&3)”将是真实的。 这种行为在Perl中可能是可以接受的,但是C / C ++是非常明确的语言。 我认为Spock的眉毛是尽职的。 :)我会附加“== 0”或“!= 0”,使其完全清楚你的目标是什么。
但无论如何,这听起来像个人喜好。 我会通过lint或类似工具运行代码,看看它是否也认为这是一个不明智的策略。 就个人而言,它就像一个编码错误。