C++中的逻辑XOR运算符?

RAC*_*RAC 274 c++ operators logical-operators

有这样的事吗?这是我第一次遇到它的实际需要,但我没有看到Stroustrup中列出的一个.我打算写:

// Detect when exactly one of A,B is equal to five.
return (A==5) ^^ (B==5);
Run Code Online (Sandbox Code Playgroud)

但是没有^^运营商.我可以在^这里使用bitwise 并得到正确的答案(无论机器表示真和假)?我从来没有拌&&&,或|||,所以我毫不犹豫地做到这一点与^^^.

我会更自在地编写自己的bool XOR(bool,bool)函数.

Gre*_*ill 507

!=运营商服务于这一目的的bool值.

  • 如果你想为`a`类型做,只需写`!(a)!=!(a)` (27认同)
  • 请注意,这仅适用于布尔值.并且^在那里工作得非常好.2!= 1 => 1这不是你想要的!正如LiraNuna所说,放一个!双方面前解决了这个问题.但又一次,你可以使用按位^ ... (13认同)
  • @David Brunelle:嗯?是什么让你认为`false!= false`会评价为'true`? (10认同)
  • 是的,我小心翼翼地提到"for bool`值",因为它不一定能做你想要的非布尔值.因为这是C++,所以存在一个真正的`bool`类型而不必为此目的使用`int`. (7认同)
  • 但是假!=假=>假 (6认同)
  • @ChrisLutz:是的,但要注意重载的运营商. (5认同)
  • 如果两者都为假,那么异或不应该返回假吗???在这种情况下,!= 将返回 true。 (2认同)
  • 大卫:F!= F ==>F。 (2认同)
  • 同样,`==` 用作一对布尔值的 XNOR。 (2认同)

Lir*_*una 231

对于真正的逻辑XOR操作,这将起作用:

if(!A != !B) {
    // code here
}
Run Code Online (Sandbox Code Playgroud)

  • 主要是将它们转换为布尔值.`!!`只会问好,但由于它们需要不同,否定它们并没有害处. (23认同)
  • 为什么不简单地使用“if (bool(A) != bool(B))”?我认为,无需否定。 (11认同)
  • @LiraNuna,“为什么A和B取反!” /“主要是将它们转换为布尔值。” -我认为这是值得一提的答案。 (9认同)
  • 我不明白为什么A和B被否定了! (6认同)
  • 不知道正常化bools的重要性花了我2天. (6认同)
  • 问题是,编译器是否能够正确优化这一点. (4认同)
  • @Ruslan 我也想说同样的话。我假设可能是从 _Bool 到 bool 的 typedef?无论如何,我意识到这个线程有多老了,但我认为 `(bool) x` 比 `!!x` 更具可读性和明确性。 (4认同)

AnT*_*AnT 42

适当的手动逻辑 XOR实现取决于您希望模拟其他逻辑运算符(||&&)与XOR 的一般行为的接近程度.关于这些运算符有两个重要的事项:1)它们保证短路评估,2)它们引入序列点,3)它们仅评估它们的操作数一次.

如您所知,XOR评估不能被短路,因为结果总是取决于两个操作数.所以1是不可能的.但是2呢?如果你不关心2,那么使用标准化(即bool)值运算符!=就结果执行XOR的工作.!如果需要,操作数可以很容易地用一元标准化.因此!A != !B在这方面实现了适当的XOR.

但是如果你关心额外的序列点,那么实现XOR的正确方法既不是!=也不^是按位.正确执行XOR(a,b)的一种可能方法可能如下所示

a ? !b : b
Run Code Online (Sandbox Code Playgroud)

这实际上就像你可以制作一个自制的XOR"相似" ||&&.当然,只有将XOR实现为宏时,这才有效.函数不会这样做,因为排序不适用于函数的参数.

有人可能会说,虽然,具有每一个序列点的唯一原因&&||是支持短路评价,从而XOR并不需要一个.实际上,这是有道理的.然而,值得考虑在中间进行具有序列点的XOR.例如,以下表达式

++x > 1 && x < 5
Run Code Online (Sandbox Code Playgroud)

已经在C/C++中定义了行为和特定结果(至少在排序方面).因此,可以合理地期望用户定义的逻辑 XOR相同,如

XOR(++x > 1, x < 5)
Run Code Online (Sandbox Code Playgroud)

!=基于a 的XOR没有这个属性.

  • @Craig McQueen:我不会错过它.我的帖子的第二段提到了它.在我看来,将操作数视为布尔值并不是逻辑运算符的关键特征,因为它们不会仅仅因为这个原因而被引入.它们被引入的主要原因是短路评估和所需的序列点. (2认同)
  • 由于 XOR 不能短路,因此不需要序列点。在这方面,XOR 更像是 +。除非您还想主张 (++x) + x 等于 2x+1,否则序列点是不合理的。 (2认同)

Ber*_*een 26

还有另一种方法可以做XOR:

bool XOR(bool a, bool b)
{
    return (a + b) % 2;
}
Run Code Online (Sandbox Code Playgroud)

显然可以证明通过以下方式工作:

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这种方法可以生成一些相当慢的代码 - 使用libstdc ++在clang和gcc上比(!a)!=(!b)慢3-5倍:http://quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q (6认同)
  • @xxaxxon 您的基准测试不正确:基准测试的两个函数之一在其循环内创建一个(不相关的)字符串。去掉这个,代码仅慢了 30%。https://quick-bench.com/q/umQRhhr0ZVS2o03fhCQAfN3HLak (2认同)

Meh*_*ari 18

XOR运算符不能短路; 即,仅通过评估其左手操作数,您无法预测XOR表达式的结果.因此,没有理由提供^^版本.

  • -1因为&&和&之间的主要区别不仅仅是短路.1 && 2为True,但1和2为假.短路只是一个方便的副作用. (25认同)
  • 那么你应该阅读Dennis Ritchie对它不存在的原因的回答:http://www.it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html (13认同)
  • 答案不是在讨论`&&`和`&`.它的意思是没有理由引入"^^".我怀疑,`^^`将任何非空值视为'1'的属性并不真正有用.或者至少我看不出任何用处. (6认同)
  • 还有C++!=其他语言.在如上所示的C和C++中,短路不仅仅是一些很好的东西,但它从根本上讲是非常重要的.:) (6认同)
  • @RAC:实际上,知道这件事很重要.这就是为什么像`if(x!= NULL && x-> IsValid())`正常工作的原因.使用`&`,它会尝试评估`x-> IsValid()`,即使```指针是'null`. (4认同)
  • 这是丹尼斯·里奇关于它为何不存在的答案的工作链接:http://c-faq.com/misc/xor.dmr.html (4认同)
  • 为了澄清,这意味着要检查XOR,您必须评估测试的两个部分。因此,拥有符号没有优势。如果在第一次测试后知道超量结果,则允许C / C ++跳过比较的不必要部分。 (2认同)
  • @Brian Postow:不,如果引入这些运算符只是为了让它们成为"逻辑"运算符,我们毫无疑问也会看到逻辑XOR引入(为什么不呢?).或者我们根本看不到任何逻辑运算符(使用`&`,`|`和`^`进行手动规范化).事实上,我们有"&&"和"||"而没有别的东西显然意味着引入这些的主要原因是短路评估. (2认同)
  • 对不起,它是`_Bool`,而不是`Bool_`. (2认同)

ele*_*ice 13

发布了一些好的代码,比#!=!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
Run Code Online (Sandbox Code Playgroud)


小智 6

使用简单:

return ((op1 ? 1 : 0) ^ (op2 ? 1 : 0));
Run Code Online (Sandbox Code Playgroud)


小智 5

我认为这是您在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
}
Run Code Online (Sandbox Code Playgroud)

证明

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
Run Code Online (Sandbox Code Playgroud)

证明是,对输入和输出的详尽研究表明,在两个表中,对于每个输入集,结果始终在两个表中相同。

因此,最初的问题是如何写:

return (A==5) ^^ (B==5)
Run Code Online (Sandbox Code Playgroud)

答案是

return (A==5) == !(B==5);
Run Code Online (Sandbox Code Playgroud)

或者,如果您愿意,写

return !(A==5) == (B==5);
Run Code Online (Sandbox Code Playgroud)


Eat*_*oes 5

(A || B) && !(A && B)

第一部分是A OR B,即Inclusive OR;第二部分是,NOT A AND B。你一起得到 A 或 B,但不能同时得到 A 和 B。

这将提供在下面的真值表中证明的 XOR。

|-----|-----|-----------|
|  A  |  B  |  A XOR B  |
|-----|-----|-----------|
|  T  |  T  |   False   |
|-----|-----|-----------|
|  T  |  F  |   True    |
|-----|-----|-----------|
|  F  |  T  |   True    |
|-----|-----|-----------|
|  F  |  F  |   False   |
|-----|-----|-----------|
Run Code Online (Sandbox Code Playgroud)

  • @xaxxon:看不出这个基准测试的意义!?在 StringCreation 中,您用于创建字符串,但在第二个基准测试中,您没有使用。如果您在两个基准测试中放置相同的代码,并为每个基准调用不同的 XOR(XOR 和 XOR2),您将获得相同的基准测试结果。那么你一直想说什么? (4认同)