Logischer XOR-Operator in C ++?

292

Gibt es so etwas? Es ist das erste Mal, dass ich einen praktischen Bedarf dafür habe, aber ich sehe keinen in Stroustrup . Ich habe vor zu schreiben:

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

Es gibt aber keinen ^^Operator. Kann ich ^hier das bitweise verwenden und die richtige Antwort erhalten (unabhängig von der maschinellen Darstellung von wahr und falsch)? Ich mische nie &und &&, oder |und ||, also zögere ich, das mit ^und zu tun ^^.

Ich würde es mir bequemer machen, bool XOR(bool,bool)stattdessen meine eigene Funktion zu schreiben .

RAC
quelle
57
Eigentlich, Jim, das ist nicht der einzige Unterschied zwischen & und && zum Beispiel ... 1 && 2 ist wahr. aber 1 & 2 => 0. Aus diesem Grund denke ich, dass "Kurzschluss" nur eine Eigenschaft ist, die sie zufällig haben. Logisch ist das wichtigere Merkmal ...
Brian Postow
6
Ganz zu schweigen davon, dass 2 && 3 == wahr ist, aber 2 & 3 == 2.
David Thornley
1
David Thomley: Nun ja, aber 2 ==> wahr, also ist das in Ordnung ... Denken Sie daran, es gibt wirklich keine Booleschen Werte ...
Brian Postow
13
@BrianPostow: Eigentlich gibt es in C ++.
Adrian Willenbücher
7
Wie unten gepostet, hier ist Dennis Ritchies Antwort, warum es nicht existiert: c-faq.com/misc/xor.dmr.html
Tobia

Antworten:

536

Der !=Operator dient diesem Zweck für boolWerte.

Greg Hewgill
quelle
7
Aber falsch! = Falsch => falsch
Jonas
13
Beachten Sie, dass dies nur für Boolesche Werte funktioniert. Und ^ würde dort perfekt funktionieren. 2! = 1 => 1 was nicht das ist was du willst! wie LiraNuna sagt, ein! vor beiden Seiten löst dieses Problem. aber wieder, dann können Sie bitweise verwenden ^ ...
Brian Postow
8
Richtig, ich habe sorgfältig darauf geachtet, "für boolWerte" zu erwähnen, weil es nicht unbedingt das tut, was Sie sich für Nicht-Boolesche Werte wünschen. Und da dies C ++ ist, gibt es einen echten boolTyp, anstatt ihn intfür diesen Zweck verwenden zu müssen.
Greg Hewgill
29
Wenn Sie es für Typ tun möchten, aschreiben Sie einfach!(a) != !(a)
Chris Lutz
6
@ ChrisLutz: Ja, aber Vorsicht vor überlasteten Operatoren.
Rwong
246

Für eine echte logische XOR-Operation funktioniert dies:

if(!A != !B) {
    // code here
}

Beachten !Sie, dass die Werte in Boolesche Werte konvertiert und negiert werden sollen, sodass zwei ungleiche positive Ganzzahlen (jeweils a true) ausgewertet werden false.

LiraNuna
quelle
6
Ich verstehe nicht, warum A und B mit negiert werden!
Martin Hansen
26
Hauptsächlich, um sie in Boolesche Werte umzuwandeln. !!würde funktionieren, fragen Sie einfach gut, aber da sie unterschiedlich sein müssen, schadet es nicht, sie zu negieren.
LiraNuna
4
Die Frage ist, ob Compiler dies richtig optimieren können.
Einpoklum
6
Nicht zu wissen, wie wichtig es ist, Bools zu normalisieren, hat mich 2 Tage gekostet.
Makan Tayebi
9
@LiraNuna, "warum A und B mit negiert werden!" / "Hauptsächlich, um sie in Boolesche Werte umzuwandeln." - Ich denke, das ist in der Antwort erwähnenswert.
cp.engr
42

Die ordnungsgemäße manuelle logische XOR-Implementierung hängt davon ab, wie genau Sie das allgemeine Verhalten anderer logischer Operatoren ( ||und &&) mit Ihrem XOR nachahmen möchten . Diese Operatoren haben zwei wichtige Eigenschaften: 1) Sie garantieren eine Kurzschlussbewertung, 2) sie führen einen Sequenzpunkt ein, 3) sie bewerten ihre Operanden nur einmal.

Wie Sie verstehen, kann die XOR-Auswertung nicht kurzgeschlossen werden, da das Ergebnis immer von beiden Operanden abhängt. 1 kommt also nicht in Frage. Aber was ist mit 2? Wenn Sie sich nicht für 2 interessieren, dann mit normalisiert (dhbool!= erledigt der Operator ) Werten die Aufgabe von XOR in Bezug auf das Ergebnis. Und die Operanden können bei Bedarf leicht mit unär normalisiert werden !. Somit !A != !Bimplementiert die richtige XOR in dieser Hinsicht.

Aber wenn Sie sich für den zusätzlichen Sequenzpunkt interessieren, auch nicht != noch bitweise ^der richtige Weg, um XOR zu implementieren. Eine Möglichkeit, XOR (a, b) korrekt auszuführen, könnte wie folgt aussehen

a ? !b : b

Dies ist tatsächlich so nah wie möglich daran, ein hausgemachtes XOR "ähnlich" zu ||und zu machen &&. Dies funktioniert natürlich nur, wenn Sie Ihr XOR als Makro implementieren. Eine Funktion funktioniert nicht, da die Sequenzierung nicht auf die Argumente der Funktion angewendet wird.

Jemand könnte jedoch sagen, dass der einzige Grund, an jedem einen Sequenzpunkt zu haben &&und darin ||besteht, die kurzgeschlossene Auswertung zu unterstützen, und XOR daher keinen benötigt. Das macht eigentlich Sinn. Es lohnt sich jedoch, ein XOR mit einem Sequenzpunkt in der Mitte in Betracht zu ziehen. Zum Beispiel der folgende Ausdruck

++x > 1 && x < 5

hat das Verhalten und das spezifizierte Ergebnis in C / C ++ definiert (zumindest in Bezug auf die Sequenzierung). Man könnte also vernünftigerweise dasselbe von benutzerdefiniertem logischem XOR erwarten wie in

XOR(++x > 1, x < 5)

Ein !=XOR auf Basis hat diese Eigenschaft nicht.

Ameise
quelle
1
Sie vermissen die andere wichtige Sache über ||und &&: C) sie bewerten die Operanden in einem booleschen Kontext. Das heißt, 1 && 2ist wahr, im Gegensatz zu 1 & 2dem , was Null ist. Ebenso ^^könnte ein Operator nützlich sein, um diese zusätzliche Funktion bereitzustellen und die Operanden in einem booleschen Kontext auszuwerten. ZB 1 ^^ 2ist falsch (anders als 1 ^ 2).
Craig McQueen
2
@Craig McQueen: Ich vermisse es nicht. Der zweite Absatz meines Beitrags erwähnt es. Meiner Meinung nach ist die Behandlung von Operanden als boolesche Werte kein kritisches Merkmal logischer Operatoren, da sie allein aus diesem Grund nicht eingeführt würden. Der Hauptgrund für ihre Einführung ist die kurzgeschlossene Auswertung und der dafür erforderliche Sequenzpunkt.
Am
Würde Ihr Vorschlag heutzutage immer noch nur mit einem Makro funktionieren? Obwohl die Reihenfolge der in einer Funktion auszuwertenden Parameter vom Compiler abhängt, ist es derzeit nicht selten, dass sie sich von links nach rechts unterscheiden. Es kann auch erwähnenswert sein, hier in den Kommentaren zu erwähnen, dass #define XOR(ll,rr) { ll ? !rr : rr }ein Aufruf wie int x = 2; XOR(++x > 1, x < 5);das falsche Ergebnis liefert, wenn eine Implementierung so aussieht . Der Aufruf müsste zusätzliche Klammern wie in enthalten int x = 2; XOR( (++x > 1), (x < 5) );, um das richtige erwartete Ergebnis zu erzielen.
RAs
1
Da XOR nicht kurzschließen kann, ist kein Sequenzpunkt erforderlich. XOR ist in dieser Hinsicht eher +. Wenn Sie nicht auch dafür argumentieren möchten, dass (++ x) + x gleich 2x + 1 ist, ist der Sequenzpunkt nicht sinnvoll.
hkBst
1
@hkBst: Ich denke, dies wird im zweiten Teil meiner Antwort vollständig behandelt.
Am
25

Es gibt eine andere Möglichkeit, XOR auszuführen:

bool XOR(bool a, bool b)
{
    return (a + b) % 2;
}

Was offensichtlich gezeigt werden kann, funktioniert über:

#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;
}
Bertie Wheen
quelle
7
Dieser Ansatz kann mit libstdc ++ ziemlich schnellen Code erzeugen - 3-5x langsamer als (! A)! = (! B) auf clang und gcc: quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q
xaxxon
18

Der XOR-Operator kann nicht kurzgeschlossen werden. Das heißt, Sie können das Ergebnis eines XOR-Ausdrucks nicht einfach durch Auswerten seines linken Operanden vorhersagen. Daher gibt es keinen Grund, eine ^^Version bereitzustellen .

Mehrdad Afshari
quelle
25
-1, weil der Hauptunterschied zwischen && und & nicht nur der Kurzschluss ist. 1 && 2 ist wahr, aber 1 & 2 ist falsch. Kurzschluss ist nur ein praktischer Nebeneffekt.
Brian Postow
6
Die Antwort spricht nicht über &&und &überhaupt. Es geht darum, dass es keinen Grund gibt, sich vorzustellen ^^. Die Eigenschaft, ^^die einen Wert ungleich Null als 1nicht wirklich nützlich betrachten würde, vermute ich. Zumindest kann ich keine Verwendung sehen.
Johannes Schaub - litb
6
Auch C ++! = Andere Sprachen. In C und C ++, wie oben gezeigt, ist Kurzschluss nicht nur ein schönes Zeug, sondern von grundlegender Bedeutung. :)
Johannes Schaub - litb
13
Dann sollten Sie Dennis Ritchies Antwort lesen, warum es sie nicht gibt: it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
Johannes Schaub - litb
5
Hier ist ein funktionierender Link zu Dennis Ritchies Antwort, warum es ihn nicht gibt: c-faq.com/misc/xor.dmr.html
Tobia
13

Es wurde ein guter Code veröffentlicht, der das Problem besser löste als! A! =! B.

Beachten Sie, dass ich BOOL_DETAIL_OPEN / CLOSE hinzufügen musste, damit es unter MSVC 2010 funktioniert

/* 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
elegante Würfel
quelle
6

Verwenden Sie eine einfache:

return ((op1 ? 1 : 0) ^ (op2 ? 1 : 0));
tiands
quelle
5

Ich denke, Sie schreiben einen XOR-Vergleich in C ++:

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
}

Beweis

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

Der Beweis ist, dass eine umfassende Untersuchung der Ein- und Ausgänge zeigt, dass in den beiden Tabellen für jeden Eingabesatz das Ergebnis in den beiden Tabellen immer identisch ist.

Daher war die ursprüngliche Frage, wie man schreibt:

return (A==5) ^^ (B==5)

Die Antwort wäre

return (A==5) == !(B==5);

Oder wenn Sie möchten, schreiben Sie

return !(A==5) == (B==5);
Indinfer
quelle
! a! =! b scheint schöner zu sein, weil es Ihre Argumente für Sie in bool umwandelt.
Xaxxon
3

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

Der erste Teil ist A ODER B, welches das Inklusive ODER ist; Der zweite Teil ist NICHT A UND B. Zusammen erhalten Sie A oder B, aber nicht sowohl A als auch B.

Dies liefert den in der nachstehenden Wahrheitstabelle nachgewiesenen XOR.

|-----|-----|-----------|
|  A  |  B  |  A XOR B  |
|-----|-----|-----------|
|  T  |  T  |   False   |
|-----|-----|-----------|
|  T  |  F  |   True    |
|-----|-----|-----------|
|  F  |  T  |   True    |
|-----|-----|-----------|
|  F  |  F  |   False   |
|-----|-----|-----------|
Iss bei Joes
quelle
Ich bin nicht die Leistung auf diesem Ansatz zu graben: quick-bench.com/PgNgGN8ATrKt7el1dAaJj7QtuF4
xaxxon
Es ist nicht nötig, defensiv zu sein.
Xaxxon
1
@xaxxon: Sehen Sie den Punkt in diesen Benchmarks nicht!? In StringCreation haben Sie beim Erstellen eines Strings verwendet, im zweiten Benchmark jedoch nicht. Wenn Sie in beiden Benchmarks identischen Code einfügen und für jeden Benchmark (XOR und XOR2) unterschiedliche XORs aufrufen, erhalten Sie dieselben Benchmark-Ergebnisse. Also, was hast du versucht zu sagen?
SoLaR
1

Ich verwende "xor" (es scheint ein Schlüsselwort zu sein; in Code :: Blocks wird es zumindest fett), genauso wie Sie "und" anstelle von &&und "oder" anstelle von verwenden können ||.

if (first xor second)...

Ja, es ist bitweise. Es tut uns leid.

csiz
quelle
2
Ich vermute, dass diese #defines von irgendwoher versteckt sind. Ich bin mir ziemlich sicher, dass "und" und "xor" keine Schlüsselwörter in ansi C sind ... (zumindest nicht C79)
Brian Postow
1
@ Brian Postow: Ich weiß nicht, was C79 ist, aber in C ++ 98 andund xorsind Standardbibliotheksmakros. Sie kommen nicht von "irgendwo", sondern von <iso646.h>. Diese Makros befinden sich auch in C99 (nicht sicher über C89 / 90).
Am
5
@ Brian Postow: ... xorsteht für bitweises xor, während andes logisch ist und.
Am
1
Ich habe C89 als C79 falsch eingegeben ... und und xor usw. sind nicht in meiner Kopie von K & R enthalten. Ich glaube nicht, dass ich jemals iso686.h verwendet habe, zumindest nicht wissentlich ... und so, ja, es sind #defines von irgendwoher, du weißt zufällig, wo das irgendwo ist B-)
Brian Postow
2
Sie sind sicherlich in C99 und verwenden diesen Header. In C ++ sind sie als "alternative Token" in die Sprache integriert, und Sie können struct A { compl A() { } };beispielsweise einen Destruktor definieren.
Johannes Schaub - litb
0
#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);
}

Es funktioniert wie definiert. Die Bedingungen sind zu erkennen, ob Sie Objective-C verwenden , das nach BOOL anstelle von bool fragt (die Länge ist unterschiedlich!)

Maxthon Chan
quelle
3
Dies verstößt gegen die doppelte Unterstrichregel.
Tamás Szelei
@ TamásSzelei Nicht unbedingt, da der Compiler dies nicht sieht, da es vorverarbeitet wurde und in der Objective-C-Welt doppelte Unterstriche ziemlich häufig sind.
Maxthon Chan
Guter Punkt zum Präprozessor, obwohl es für mich immer noch so nach Code riecht (warum sollte man überhaupt ein Makro anstelle eines typedef verwenden?). Die Frage betraf auch nicht Objective-C.
Tamás Szelei
@ TamásSzelei Nun, ich hatte die Angewohnheit, Header-Dateien in mehreren Sprachen zu teilen, und normalerweise stammen alle Header von Objective-C. Mein neuer Code riecht jetzt nicht zu viel, aber der doppelte Unterstrich wird immer noch von Zeit zu Zeit verwendet, um die ObjC-Gewohnheiten einzuhalten.
Maxthon Chan