是否有任何理由不使用按位运算符 &、| 和 ^ 作为 C++ 中的“布尔”值?
我有时会遇到我希望两个条件之一为真(XOR)的情况,所以我只是将 ^ 运算符放入条件表达式中。我有时还希望评估条件的所有部分是否结果为真(而不是短路),所以我使用 & 和 |。有时我还需要累积布尔值,&= 和 |= 可能非常有用。
这样做时,我引起了一些人的注意,但是代码仍然比其他方式更有意义和更清晰。有什么理由不将这些用于布尔值吗?是否有任何现代编译器对此给出不好的结果?
是否有任何理由不使用按位运算符 &、| 和 ^ 作为 C++ 中的“布尔”值?
我有时会遇到我希望两个条件之一为真(XOR)的情况,所以我只是将 ^ 运算符放入条件表达式中。我有时还希望评估条件的所有部分是否结果为真(而不是短路),所以我使用 & 和 |。有时我还需要累积布尔值,&= 和 |= 可能非常有用。
这样做时,我引起了一些人的注意,但是代码仍然比其他方式更有意义和更清晰。有什么理由不将这些用于布尔值吗?是否有任何现代编译器对此给出不好的结果?
||
and&&
是布尔运算符,内置运算符保证返回true
or 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
and true
(或0
and 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
,即到0
和1
序数值。
保证短路评估,一旦知道最终结果,表达式评估就会停止。
这可以解释为具有True、False和Indeterminate的树值逻辑。
可读的文本等价物not
,and
和or
,即使我自己不使用它们。
正如读者 Antimony 在评论中指出的那样,位级运算符也有替代标记,即bitand
,和bitor
,但在我看来,它们的可读性不如,和。xor
compl
and
or
not
简而言之,高级运算符的每一个优点都是位级运算符的缺点。
特别是,由于按位运算符缺少到 0/1 的参数转换,因此您会得到例如1 & 2
→ 0
,而1 && 2
→ true
。此外^
,按位异或,可能会以这种方式行为不端。视为布尔值 1 和 2 相同,即true
,但视为位模式它们是不同的。
然后你为这个问题提供一点背景,
“我有时会遇到我希望两个条件之一为真(异或)的情况,所以我只是将 ^ 运算符放入条件表达式中。”
好吧,按位运算符的优先级高于逻辑运算符。这尤其意味着在混合表达式中,例如
a && b ^ c
你可能会得到意想不到的结果a && (b ^ c)
。
而是只写
(a && b) != c
更简洁地表达你的意思。
对于多参数或者/或者没有 C++ 运算符可以完成这项工作。例如,如果您写a ^ b ^ c
的不是“要么a
,b
要么c
为真”的表达式。取而代之的是,“奇数个a
,b
并且c
是真的”,可能是其中 1 个或全部 3 个……
要表达一般的 else/or when a
,b
andc
是 type bool
,只需写
(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 输出:
真的 错误的
结果不同的原因是位级别&=
不提供bool
其右侧参数的转换。
那么,您希望使用哪些结果&=
?
如果是前者,true
那么最好定义一个运算符(例如,如上所述)或命名函数,或者使用右侧表达式的显式转换,或者完整地编写更新。
与帕特里克的回答相反,C++ 没有^^
用于执行短路异或的运算符。如果你想一想,^^
无论如何都有一个运算符是没有意义的:使用异或,结果总是取决于两个操作数。然而,帕特里克关于非bool
“布尔”类型的警告1 & 2
与1 && 2
. 一个典型的例子是 WindowsGetMessage()
函数,它返回一个三态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'语句的条件(尽管这似乎最有可能),这是我的假设。但我对中间布尔值的建议仍然有效。
对 bool 使用按位运算有助于节省处理器不必要的分支预测逻辑,这是由逻辑运算引入的“cmp”指令产生的。
用按位运算(所有操作数都是布尔值)替换逻辑会生成更高效的代码,提供相同的结果。理想情况下,效率应该超过使用逻辑操作在排序中可以利用的所有短路优势。
这可能会使代码有点不可读,尽管程序员应该注释它并说明这样做的原因。
IIRC,许多 C++ 编译器在尝试将按位运算的结果转换为布尔值时会发出警告。您必须使用类型转换才能使编译器满意。
在 if 表达式中使用按位运算会引起同样的批评,尽管编译器可能不会。任何非零值都被认为是真的,所以像“if (7 & 3)”这样的东西将是真的。这种行为在 Perl 中可能是可以接受的,但 C/C++ 是非常明确的语言。我认为斯波克眉毛是尽职调查。:) 我会附加“== 0”或“!= 0”以完全清楚您的目标是什么。
但无论如何,这听起来像是个人喜好。我会通过 lint 或类似工具运行代码,看看它是否也认为这是一个不明智的策略。就个人而言,它读起来像是一个编码错误。