C ++中的逻辑异或运算符?
Posted
技术标签:
【中文标题】C ++中的逻辑异或运算符?【英文标题】:Logical XOR operator in C++? 【发布时间】:2010-12-08 11:23:35 【问题描述】:有这种事吗?这是我第一次遇到对它的实际需求,但我没有看到列出in Stroustrup。我打算写:
// Detect when exactly one of A,B is equal to five.
return (A==5) ^^ (B==5);
但是没有^^
运算符。我可以在这里使用按位^
并得到正确的答案(不管机器表示真假)吗?我从不将&
和&&
或|
和||
混合使用,所以我犹豫要不要使用^
和^^
。
我会更自在地编写自己的 bool XOR(bool,bool)
函数。
【问题讨论】:
实际上,Jim,这不是 & 和 && 之间的唯一区别,例如... 1 && 2 是 True。但是 1 & 2 => 0。因此,我认为“短路”只是他们碰巧拥有的属性。逻辑是更重要的特征... 不要说 2 && 3 == true,而是 2 & 3 == 2。 David Thomley:嗯,是的,但是 2 ==> 是的,所以没关系...记住,真的没有任何布尔值... @BrianPostow:实际上,在 C++ 中有。 如下所示,这是丹尼斯·里奇关于为什么它不存在的答案:c-faq.com/misc/xor.dmr.html 【参考方案1】:!=
运算符为bool
值服务。
【讨论】:
但是假!=假=>假 请注意,这只适用于布尔值。 ^ 在那里工作得很好。 2 !=1 => 1 这不是你想要的!正如 LiraNuna 所说,放一个 !在双方面前解决了这个问题。但同样,你可以使用按位 ^... 对,我很小心地提到“对于bool
值”,因为它不一定能满足您对非布尔值的要求。由于这是 C++,因此存在一个真正的 bool
类型,而不必为此使用 int
。
如果你想输入a
,只需写!(a) != !(a)
@ChrisLutz:是的,但要小心重载运算符。【参考方案2】:
对于真正的逻辑异或运算,这将起作用:
if(!A != !B)
// code here
注意!
用于将值转换为布尔值并将它们取反,以便两个不相等的正整数(每个true
)的计算结果为false
。
【讨论】:
我不明白为什么A和B被否定了! 主要是将它们转换为布尔值。!!
问得好就行,但由于它们需要不同,因此否定它们并没有害处。
问题是,编译器是否能够正确优化这一点。
不知道标准化布尔值的重要性让我花了 2 天时间。
@LiraNuna,“为什么 A 和 B 被否定了!” /“主要是将它们转换为布尔值。” - 我认为这值得在答案中提及。【参考方案3】:
正确的手动逻辑 XOR 实现取决于您希望用 XOR 模拟其他逻辑运算符(||
和 &&
)的一般行为的程度。这些操作符有两点很重要:1)它们保证短路计算,2)它们引入了一个序列点,3)它们只对它们的操作数进行一次计算。
正如您所理解的,XOR 评估不能短路,因为结果始终取决于两个操作数。所以1是没有问题的。但是2呢?如果您不关心 2,那么使用归一化(即bool
)值运算符!=
在结果方面执行 XOR 的工作。如有必要,操作数可以很容易地用一元 !
标准化。因此!A != !B
在这方面实现了正确的异或。
但如果您关心额外的序列点,!=
和按位^
都不是实现 XOR 的正确方法。正确执行 XOR(a, b) 的一种可能方法如下所示
a ? !b : b
这实际上与制作与||
和&&
“相似”的自制XOR 非常接近。当然,这只有在您将 XOR 实现为宏时才有效。函数不会这样做,因为排序不适用于函数的参数。
尽管有人可能会说,在每个&&
和||
上都有一个序列点的唯一原因是为了支持短路评估,因此不需要异或。实际上,这是有道理的。然而,值得考虑在中间有一个序列点的 XOR。例如下面的表达式
++x > 1 && x < 5
在 C/C++ 中定义了行为和特定结果(至少在排序方面)。因此,人们可能会合理地期望用户定义的 logical XOR 相同,如
XOR(++x > 1, x < 5)
虽然基于 !=
的 XOR 没有此属性。
【讨论】:
你错过了关于||
和&&
的另一个重要的事情:C)它们在布尔上下文中评估操作数。也就是说,1 && 2
是真的,不像 1 & 2
是零。同样,^^
运算符可用于提供此额外功能,即在布尔上下文中评估操作数。例如。 1 ^^ 2
为假(与 1 ^ 2
不同)。
@Craig McQueen:我没有错过它。我帖子的第二段提到了它。在我看来,将操作数视为布尔值并不是逻辑运算符的一个关键特征,从某种意义上说,它们不会仅仅因为这个原因而被引入。引入它们的主要原因是短路评估和所需的序列点。
现在,您的建议是否仍然只适用于宏?尽管函数中要评估的参数顺序确实取决于编译器,但目前从左到右不同的情况不是很少吗?此外,在 cmets 中可能值得注意的是,如果实现看起来像 #define XOR(ll,rr) ll ? !rr : rr
,那么像 int x = 2; XOR(++x > 1, x < 5);
这样的调用将给出错误的结果。调用必须有额外的括号,例如 int x = 2; XOR( (++x > 1), (x < 5) );
,才能给出正确的预期结果。
由于 XOR 不能短路,因此不需要序列点。在这方面,XOR 更像 +。除非你也想论证 (++x) + x 等于 2x+1,否则序列点是不合理的。
@hkBst:我认为这在我的回答的第二部分中已经完全涵盖了。【参考方案4】:
还有另一种异或的方法:
bool XOR(bool a, bool b)
return (a + b) % 2;
这显然可以通过以下方式证明:
#include <iostream>
bool XOR(bool a, bool b)
return (a + b) % 2;
int main()
using namespace std;
cout << "XOR(true, true):\t" << XOR(true, true) << endl
<< "XOR(true, false):\t" << XOR(true, false) << endl
<< "XOR(false, true):\t" << XOR(false, true) << endl
<< "XOR(false, false):\t" << XOR(false, false) << endl
<< "XOR(0, 0):\t\t" << XOR(0, 0) << endl
<< "XOR(1, 0):\t\t" << XOR(1, 0) << endl
<< "XOR(5, 0):\t\t" << XOR(5, 0) << endl
<< "XOR(20, 0):\t\t" << XOR(20, 0) << endl
<< "XOR(6, 6):\t\t" << XOR(5, 5) << endl
<< "XOR(5, 6):\t\t" << XOR(5, 6) << endl
<< "XOR(1, 1):\t\t" << XOR(1, 1) << endl;
return 0;
【讨论】:
这种方法可以生成一些非常慢的代码 - 在使用 libstdc++ 的 clang 和 gcc 上比 (!a) != (!b) 慢 3-5 倍:quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q @xaxxon 您的基准测试不正确:基准测试的两个函数之一在其循环内创建了一个(不相关的)字符串。去掉这个,代码只慢了 30%。 quick-bench.com/q/umQRhhr0ZVS2o03fhCQAfN3HLak @gmargari 一定忘记删除它开始时自动生成的基准。对不起。【参考方案5】:XOR 运算符不能短路;也就是说,您不能仅通过评估其左侧操作数来预测 XOR 表达式的结果。因此,没有理由提供^^
版本。
【讨论】:
-1 因为 && 和 & 之间的主要区别不仅仅是短路。 1 && 2 为真,但 1 & 2 为假。短路只是一个方便的副作用。 答案根本不是&&
和&
。它的重点是没有理由介绍^^
。我怀疑^^
将任何非空值视为1
的属性并不是真正有用。或者至少我看不到任何用处。
还有 C++ != 其他语言。在 C 和 C++ 中,如上所示,短路不仅仅是一些不错的东西,而且它从根本上来说很重要。 :)
那么你应该阅读 Dennis Ritchie 关于它为什么不存在的答案:it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
这是丹尼斯·里奇关于为什么它不存在的答案的工作链接:c-faq.com/misc/xor.dmr.html【参考方案6】:
发布了一些比 !a != !b 更好地解决问题的好代码
请注意,我必须添加 BOOL_DETAIL_OPEN/CLOSE 才能在 MSVC 2010 上运行
/* From: http://groups.google.com/group/comp.std.c++/msg/2ff60fa87e8b6aeb
Proposed code left-to-right? sequence point? bool args? bool result? ICE result? Singular 'b'?
-------------- -------------- --------------- ---------- ------------ ----------- -------------
a ^ b no no no no yes yes
a != b no no no no yes yes
(!a)!=(!b) no no no no yes yes
my_xor_func(a,b) no no yes yes no yes
a ? !b : b yes yes no no yes no
a ? !b : !!b yes yes no no yes no
[* see below] yes yes yes yes yes no
(( a bool_xor b )) yes yes yes yes yes yes
[* = a ? !static_cast<bool>(b) : static_cast<bool>(b)]
But what is this funny "(( a bool_xor b ))"? Well, you can create some
macros that allow you such a strange syntax. Note that the
double-brackets are part of the syntax and cannot be removed! The set of
three macros (plus two internal helper macros) also provides bool_and
and bool_or. That given, what is it good for? We have && and || already,
why do we need such a stupid syntax? Well, && and || can't guarantee
that the arguments are converted to bool and that you get a bool result.
Think "operator overloads". Here's how the macros look like:
Note: BOOL_DETAIL_OPEN/CLOSE added to make it work on MSVC 2010
*/
#define BOOL_DETAIL_AND_HELPER(x) static_cast<bool>(x):false
#define BOOL_DETAIL_XOR_HELPER(x) !static_cast<bool>(x):static_cast<bool>(x)
#define BOOL_DETAIL_OPEN (
#define BOOL_DETAIL_CLOSE )
#define bool_and BOOL_DETAIL_CLOSE ? BOOL_DETAIL_AND_HELPER BOOL_DETAIL_OPEN
#define bool_or BOOL_DETAIL_CLOSE ? true:static_cast<bool> BOOL_DETAIL_OPEN
#define bool_xor BOOL_DETAIL_CLOSE ? BOOL_DETAIL_XOR_HELPER BOOL_DETAIL_OPEN
【讨论】:
【参考方案7】:使用简单的:
return ((op1 ? 1 : 0) ^ (op2 ? 1 : 0));
【讨论】:
【参考方案8】:以下是我认为您在 C++ 中编写 XOR 比较的方式:
bool a = true; // Test by changing to true or false
bool b = false; // Test by changing to true or false
if (a == !b) // THIS IS YOUR XOR comparison
// do whatever
证明
XOR TABLE
a b XOR
--- --- ---
T T F
T F T
F T T
F F F
a == !b TABLE
a b !b a == !b
--- --- --- -------
T T F F
T F T T
F T F T
F F T F
证据是对输入和输出的详尽研究表明,在这两个表中,对于每个输入集,两个表中的结果总是相同的。
因此,最初的问题是如何编写:
return (A==5) ^^ (B==5)
答案是
return (A==5) == !(B==5);
或者,如果你喜欢,写
return !(A==5) == (B==5);
【讨论】:
!a != !b 似乎更好,因为它会将您的参数转换为布尔值。【参考方案9】:(A || B) && !(A && B)
第一部分是A OR B,即Inclusive OR;第二部分是,不是 A 和 B。加在一起你会得到 A 或 B,但不能同时得到 A 和 B。
这将提供在下面的真值表中证明的 XOR。
|-----|-----|-----------|
| A | B | A XOR B |
|-----|-----|-----------|
| T | T | False |
|-----|-----|-----------|
| T | F | True |
|-----|-----|-----------|
| F | T | True |
|-----|-----|-----------|
| F | F | False |
|-----|-----|-----------|
【讨论】:
我不会挖掘这种方法的性能:quick-bench.com/PgNgGN8ATrKt7el1dAaJj7QtuF4 没有必要为此辩护。 @xaxxon:在这个基准测试中看不到重点!?在 StringCreation 中,您用于创建字符串,但在第二个基准测试中您没有。如果您在两个基准测试中放置相同的代码,并为每个基准测试(XOR 和 XOR2)调用不同的 XOR,您将获得相同的基准测试结果。那你想说什么?【参考方案10】:我使用“xor”(它似乎是一个关键字;在Code::Blocks 中至少它变得粗体)就像您可以使用“and”而不是&&
和“or”而不是||
。
if (first xor second)...
是的,它是按位的。对不起。
【讨论】:
我猜这些是从某个地方隐藏的#defines。我很确定“and”和“xor”不是ansi C中的关键字......(至少不是C79) @Brian Postow:我不知道 C79 是什么,但在 C++98 中and
和 xor
是标准库宏。他们不是来自“某处”,而是来自xor
代表 bitwise xor,而 and
是 logical and.
我将 C89 错误地输入为 C79... 并且 xor 等不在我的 K&R 副本中。我不认为我曾经使用过 iso686.h,至少在不知情的情况下......所以,是的,它们是来自某个地方的 #defines,你只是碰巧知道某个地方在哪里 B-)
他们肯定在 C99 中使用该标头。在 C++ 中,它们作为“替代标记”集成到语言中,例如,您可以使用struct A compl A() ;
来定义析构函数。【参考方案11】:
#if defined(__OBJC__)
#define __bool BOOL
#include <stdbool.h>
#define __bool bool
#endif
static inline __bool xor(__bool a, __bool b)
return (!a && b) || (a && !b);
它按定义工作。条件是检测你是否使用Objective-C,它要求的是BOOL而不是bool(长度不同!)
【讨论】:
这违反了双下划线规则。 @TamásSzelei 不一定,因为编译器看不到这一点,因为它已被预处理掉,并且在 Objective-C 世界中双下划线相当普遍。 关于预处理器的好点,虽然对我来说它仍然是这样的代码气味(为什么要使用宏而不是 typedef?)。此外,问题与 Objective-C 无关。 @TamásSzelei 好吧,我以前有跨多种语言共享头文件的习惯,通常所有头文件都来自 Objective-C。我的新代码现在没有太多味道了,但是还是不时使用双下划线来遵守 ObjC 的习惯。以上是关于C ++中的逻辑异或运算符?的主要内容,如果未能解决你的问题,请参考以下文章