是否有任何理由不对C ++中的“布尔”值使用按位运算符&,|和^?
有时我遇到两种情况,我希望精确地满足两个条件之一(XOR),因此我只是将^运算符放入条件表达式中。有时我还希望评估条件的所有部分,无论结果是否正确(而不是短路),因此我使用&和|。有时我还需要累积布尔值,并且&=和| =可能非常有用。
在执行此操作时,我有些挑剔,但是代码仍然比其他代码有意义且更简洁。有没有理由不将其用于布尔?是否有任何现代编译器为此带来不好的结果?
是否有任何理由不对C ++中的“布尔”值使用按位运算符&,|和^?
有时我遇到两种情况,我希望精确地满足两个条件之一(XOR),因此我只是将^运算符放入条件表达式中。有时我还希望评估条件的所有部分,无论结果是否正确(而不是短路),因此我使用&和|。有时我还需要累积布尔值,并且&=和| =可能非常有用。
在执行此操作时,我有些挑剔,但是代码仍然比其他代码有意义且更简洁。有没有理由不将其用于布尔?是否有任何现代编译器为此带来不好的结果?
Answers:
||
并且&&
是布尔运算符,并且内置运算符保证返回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
保证类型只能是atrue
或a false
(分别隐式地转换为1
and 0
),因此从这种立场开始不必担心,但事实是人们不习惯在代码中看到这种情况。提出不这样做的好理由。只是说完b = b && x
就做。
两个主要原因。简而言之,请仔细考虑。这样做可能有充分的理由,但是如果您的注释中有非常明确的内容,因为它可能很脆弱,而且就像您自己说的那样,人们通常不习惯看到这样的代码。
首先,如果您要对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
是你想要的
!==
这么说),因此,如果要计算bool
值序列的XOR,则需要acc = acc!= condition(i);
在循环主体中编写。编译器可能可以像处理!==
现有代码一样有效地处理它,但是有些人可能会发现它看起来并不好,并且倾向于将布尔值添加为整数,然后测试总和是否为奇数。
扬起的眉毛应该告诉你足以停止这样做。您无需为编译器编写代码,而是先为其他程序员编写代码,然后再为编译器编写代码。即使编译器正常工作,也不会让其他人感到惊讶-按位运算符是针对位操作而不是布尔值。
我想你也用叉子吃苹果吗?它可以工作,但会让人们感到惊讶,因此最好不要这样做。
你问:
“有什么理由不使用的位运算符
&
,|
以及^
对‘用C布尔’值++?”
是的,逻辑运算符,也就是内置的高级别布尔运算符!
,&&
并且||
,具有以下优点:
保证的参数转换到bool
,即0
与1
序数值。
保证短路评估,一旦知道最终结果,表达式评估就会停止。
这可以解释为具有True,False和Indeterminate的树值逻辑。
可读文本等价物not
,and
并且or
,即使我不使用他们自己。
作为评论的读者锑笔记也是位级运营商的替代标记,即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
更简洁地表达您的意思。
对于多参数,要么/或者没有C ++运算符执行该工作。例如,如果您编写的代码a ^ b ^ c
不是,则该表达式不是说“或者a
,b
或者c
是true”。相反,它说,“一个奇数a
,b
而且c
是真正的”,这可能是他们或所有3的1 ...
要表示一般的“或”或“何时a
”b
和c
“类型” bool
,只需编写
(a + b + c) == 1
或者,使用非bool
参数将其转换为bool
:
(!!a + !!b + !!c) == 1
&=
积累的布尔结果。您进一步阐述,
“有时我还需要累积布尔值,
&=
并且|=?
可能非常有用。”
好吧,这相当于分别检查是否满足所有条件或任何条件,并且摩根定律告诉您如何从一个条件转到另一个条件。即,您只需要其中之一。你可以在原则上使用*=
的&&=
-运算符(如良好的老乔治·布尔发现,逻辑,并且可以很容易地表示为乘法),但我认为,这将困扰和代码可能误导维护者。
还请考虑:
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
则最好定义一个运算符(如上)或命名函数,或者使用右侧表达式的显式转换,或者完整编写更新。
bitand
,bitor
,xor
和compl
。我认为这就是为什么我使用“可读”限定词的原因。当然,例如的可读性compl 42
是主观的。;-)
bool
”只是约定的结果,并不保证C ++语言实际上会您。
+
保证将其参数转换为unsigned
,但这并不能阻止unsigned int n=-1; n+=3.14;
实际使用double
(或它float
?)加法运算。(比较您的&=
示例。)
与Patrick的答案相反,C ++没有^^
运算符来执行短路异或运算。如果再三思索一下,那么^^
无论如何都没有一个运算符:使用“异或”运算符,结果始终取决于两个操作数。不过,帕特里克关于非警告bool
“布尔”类型的比较时持有同样1 & 2
对1 && 2
。WindowsGetMessage()
函数就是一个典型的例子,它返回三态BOOL
:非零0
,或-1
。
使用&
而不是&&
和|
代替||
不是很常见的错别字,因此,如果您故意这样做,则应在注释中说明原因。
^^
运营商仍然是有用的,无论短路的考虑。它将一)评估操作数在布尔环境和b)保证返回1或0
inline bool XOR(bool a,bool b) { return a!=b; }
和获取所需内容很简单,只是它是一个函数而不是(中缀)运算符。或者,您可以直接使用!=
或重载其他具有此含义的运算符,但是当然您需要非常小心,以免意外使用相同名称的意外重载。并且顺便||
和&&
返回true
or false
,而不是1
or 0
。
!
,||
而&&
在布尔环境评估他们的论据是唯一的,因为这些运营商很少重载接受其他类型的; 据我所知,该语言确实允许此类重载,因此不能保证在布尔上下文中评估参数。
帕特里克(Patrick)提出了要点,我不再重复。但是我可能会建议通过使用命名良好的布尔变量来尽可能地将'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'语句的条件(尽管这似乎很有可能),这是我的假设。但是我对中间布尔值的建议仍然存在。
对布尔使用按位运算有助于处理器节省不必要的分支预测逻辑,这是由逻辑运算引入的“ cmp”指令导致的。
用按位运算(所有操作数均为bool)替换逻辑会生成提供相同结果的更有效代码。理想情况下,效率应超过使用逻辑运算进行订购时可以利用的所有短路优势。
尽管程序员应该以注释的理由来注释它,但这可能会使代码有点不可读。
IIRC,许多C ++编译器会在尝试将按位运算的结果强制转换为布尔值时发出警告。您必须使用类型强制转换才能使编译器满意。
在if表达式中使用按位运算将引起相同的批评,尽管编译器可能不会这样做。任何非零值都将被视为true,因此“ if(7&3)”之类的值将为true。这种行为在Perl中是可以接受的,但是C / C ++是非常明确的语言。我认为Spock的眉毛是尽职调查。:)我将附加“ == 0”或“!= 0”以使其完全清楚您的目标是什么。
但是无论如何,这听起来像是个人喜好。我将通过lint或类似工具运行代码,看看它是否还认为这是不明智的策略。就个人而言,它读起来就像是一个编码错误。