Gibt es einen Grund, die bitweisen Operatoren &, | und ^ nicht für "bool" -Werte in C ++ zu verwenden?
Manchmal stoße ich auf Situationen, in denen genau eine von zwei Bedingungen wahr sein soll (XOR), also wirf ich den Operator ^ einfach in einen bedingten Ausdruck. Manchmal möchte ich auch, dass alle Teile einer Bedingung bewertet werden, ob das Ergebnis wahr ist oder nicht (anstatt kurzzuschließen), also verwende ich & und |. Ich muss manchmal auch Boolesche Werte akkumulieren, und & = und | = können sehr nützlich sein.
Ich habe dabei ein paar hochgezogene Augenbrauen bekommen, aber der Code ist immer noch aussagekräftig und sauberer als sonst. Gibt es einen Grund, diese NICHT für Bools zu verwenden? Gibt es moderne Compiler, die dafür schlechte Ergebnisse liefern?
quelle
Antworten:
||
und&&
sind boolesche Operatoren und die eingebauten Operatoren geben garantiert entwedertrue
oder zurückfalse
. Nichts anderes.|
,&
Und^
sind bitweise Operatoren. Wenn die Domäne der Zahlen, mit denen Sie arbeiten, nur 1 und 0 ist, sind sie genau gleich, aber in Fällen, in denen Ihre Booleschen Werte nicht streng 1 und 0 sind - wie dies bei der C-Sprache der Fall ist - kann es zu einem gewissen Verhalten kommen du wolltest nicht. Zum Beispiel:BOOL two = 2; BOOL one = 1; BOOL and = two & one; //and = 0 BOOL cand = two && one; //cand = 1
In C ++, aber die
bool
ist Typ nur gewährleistet , entweder eine seintrue
oder einfalse
(die implizit bzw. konvertieren1
und0
), so dass es von dieser Haltung weniger eine Sorge ist, aber die Tatsache , dass die Menschen zu sehen , solche Dinge in Code nicht verwendet werden macht ein gutes Argument dafür, es nicht zu tun. Sagen Sie einfachb = b && x
und fertig.quelle
Zwei Hauptgründe. Kurz gesagt, überlegen Sie genau; Es könnte einen guten Grund dafür geben, aber wenn Ihre Kommentare SEHR explizit sind, weil sie brüchig sein können und, wie Sie selbst sagen, die Leute im Allgemeinen nicht daran gewöhnt sind, Code wie diesen zu sehen.
Bitweises xor! = Logisches xor (außer 0 und 1)
Erstens kann der Operator , wenn Sie mit anderen Werten als
false
undtrue
(oder0
und1
als Ganzzahlen) arbeiten,^
ein Verhalten einführen, das keinem logischen xor entspricht. Zum Beispiel: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 }
Dank an Benutzer @Patrick, der dies zuerst ausgedrückt hat.
Reihenfolge der Operationen
Zweitens
|
,&
und^
als Bit - Operatoren, nicht kurzschließen. Darüber hinaus können mehrere bitweise Operatoren, die in einer einzigen Anweisung miteinander verkettet sind - auch mit expliziten Klammern - durch Optimieren von Compilern neu angeordnet werden, da alle drei Operationen normalerweise kommutativ sind. Dies ist wichtig, wenn die Reihenfolge der Operationen von Bedeutung ist.Mit anderen Worten
bool result = true; result = result && a() && b(); // will not call a() if result false, will not call b() if result or a() false
gibt nicht immer das gleiche Ergebnis (oder den gleichen Endzustand) wie
bool result = true; result &= (a() & b()); // a() and b() both will be called, but not necessarily in that order in an // optimizing compiler
Dies ist besonders wichtig, da Sie möglicherweise keine Methoden steuern
a()
undb()
oder jemand anderes mitkommt und sie später ändert, ohne die Abhängigkeit zu verstehen, und einen bösen (und häufig nur Release-Build) Fehler verursacht.quelle
Meiner Ansicht nach
ist was du willst
quelle
!==
sozusagen). Wenn Sie also das XOR einer Folge vonbool
Werten berechnen, müssen Sieacc = acc!= condition(i);
in einen Schleifenkörper schreiben . Der Compiler kann dies wahrscheinlich so effizient handhaben, als ob!==
es ihn gäbe , aber einige mögen feststellen, dass es nicht gut aussieht, und bevorzugen die Alternative, die Booleschen Werte als Ganzzahlen hinzuzufügen und dann zu testen, ob die Summe ungerade ist.Die hochgezogenen Augenbrauen sollten Ihnen genug sagen, um damit aufzuhören. Sie schreiben den Code nicht für den Compiler, sondern zuerst für Ihre Programmierkollegen und dann für den Compiler. Selbst wenn die Compiler funktionieren, ist es nicht das, was Sie wollen, andere Leute zu überraschen - bitweise Operatoren sind für Bitoperationen, nicht für Bools.
Ich nehme an, Sie essen auch Äpfel mit einer Gabel? Es funktioniert, aber es überrascht die Leute, also ist es besser, es nicht zu tun.
quelle
Nachteile der Bitlevel-Operatoren.
Du fragst:
Ja, die logischen Operatoren , die die integrierte High Level Booleschen Operatoren sind
!
,&&
und||
bietet folgende Vorteile:Garantierte Umwandlung von Argumenten in
bool
, dh in0
und1
Ordnungswert.Garantierte Kurzschlussbewertung, bei der die Ausdrucksbewertung beendet wird, sobald das Endergebnis bekannt ist.
Dies kann als Baumwertlogik mit True , False und Indeterminate interpretiert werden .
Lesbare Textäquivalente
not
,and
undor
auch wenn ich sie nicht selbst benutze.Als Leser Antimony Notizen in einem Kommentar auch die bitlevel Operatoren alternative Token, nämlich
bitand
,bitor
,xor
undcompl
, aber meiner Meinung nach sind diese weniger lesbar alsand
,or
undnot
.Einfach ausgedrückt ist jeder dieser Vorteile der High-Level-Operatoren ein Nachteil der Bitlevel-Operatoren.
Insbesondere, da den bitweisen Operatoren die Argumentkonvertierung in 0/1 fehlt, erhalten Sie zB
1 & 2
→0
, während1 && 2
→true
. Auch^
bitweise exklusiv oder kann sich auf diese Weise schlecht benehmen. Als boolesche Werte betrachtet, sind 1 und 2 gleichtrue
, aber als Bitmuster betrachtet sind sie unterschiedlich.Wie man logisch entweder / oder in C ++ ausdrückt .
Sie geben dann ein wenig Hintergrundwissen für die Frage,
Nun, die bitweisen Operatoren haben eine höhere Priorität als die logischen Operatoren. Dies bedeutet insbesondere, dass in einem gemischten Ausdruck wie
Sie erhalten das vielleicht unerwartete Ergebnis
a && (b ^ c)
.Schreiben Sie stattdessen einfach
präziser ausdrücken, was du meinst.
Für das Mehrfachargument gibt es entweder keinen C ++ - Operator, der den Job ausführt. Zum Beispiel, wenn Sie schreiben ,
a ^ b ^ c
als das ist nicht ein Ausdruck, der „entweder sagta
,b
oderc
ist wahr“. Stattdessen heißt es : „Eine ungerade Anzahl vona
,b
undc
wahr sind“, was 1 von ihnen sein könnte oder alle 3 ...Um auszudrücken , den allgemeinen entweder / oder , wenn
a
,b
undc
sind vom Typbool
, nur Schreib(a + b + c) == 1
oder
bool
konvertieren Sie sie mit Nichtargumenten inbool
:(!!a + !!b + !!c) == 1
Verwenden
&=
, um boolesche Ergebnisse zu akkumulieren.Sie arbeiten weiter aus,
Nun, dies entspricht der Überprüfung, ob alle oder eine Bedingung erfüllt ist, und das Gesetz von de Morgan sagt Ihnen, wie Sie von einer zur anderen gehen sollen. Dh du brauchst nur einen von ihnen. Sie könnten im Prinzip
*=
als&&=
Operator verwendet werden (denn wie der gute alte George Boole herausgefunden hat, kann logisches UND sehr leicht als Multiplikation ausgedrückt werden), aber ich denke, dass dies die Betreuer des Codes verwirren und möglicherweise irreführen würde.Beachten Sie auch:
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; }
Ausgabe mit Visual C ++ 11.0 und g ++ 4.7.1:
Der Grund für den Unterschied in den Ergebnissen ist, dass der Bitlevel
&=
keine Konvertierung inbool
sein Argument auf der rechten Seite bietet .Welches dieser Ergebnisse wünschen Sie sich für Ihre Verwendung
&=
?Wenn erstere,
true
definieren Sie besser einen Operator (z. B. wie oben) oder eine benannte Funktion oder verwenden Sie eine explizite Konvertierung des Ausdrucks auf der rechten Seite oder schreiben Sie die Aktualisierung vollständig.quelle
bitand
,bitor
,xor
undcompl
. Ich denke, deshalb habe ich die Qualifikation "lesbar" verwendet. Natürlich ist die Lesbarkeit von zBcompl 42
subjektiv. ;-)bool
“ nur eine Folge der Konvention ist, keine Garantie , dass die Sprache C ++ gibt tatsächlich Sie.+
garantiert der eingebaute (übergeordnete) Ganzzahl-Additionsoperator ohne Vorzeichen die Konvertierung seiner Argumente inunsigned
, aber dies verhindert nicht, dassunsigned int n=-1; n+=3.14;
diedouble
(oder ist esfloat
?) Additionsoperation tatsächlich verwendet wird . (Vergleichen Sie Ihr&=
Beispiel.)Im Gegensatz zu Patricks Antwort hat C ++ keinen
^^
Operator für die Durchführung eines exklusiven Kurzschlusses oder. Wenn Sie eine Sekunde darüber nachdenken, macht es^^
sowieso keinen Sinn , einen Operator zu haben: Mit exklusiv oder hängt das Ergebnis immer von beiden Operanden ab. Patricks Warnung vor nichtbool
"Booleschen" Typen gilt jedoch im Vergleich1 & 2
zu1 && 2
. Ein klassisches Beispiel hierfür ist die Windows-GetMessage()
Funktion, die einen Drei-Status zurückgibtBOOL
: ungleich Null0
, oder-1
.Die Verwendung von
&
anstelle von&&
und|
anstelle von||
ist kein ungewöhnlicher Tippfehler. Wenn Sie dies also absichtlich tun, verdient es einen Kommentar, in dem angegeben wird, warum.quelle
^^
Bediener wäre unabhängig von der Kurzschlussüberlegung immer noch nützlich. Es würde a) die Operanden in einem booleschen Kontext bewerten und b) garantieren, eine 1 oder 0 zurückzugeben.inline bool XOR(bool a,bool b) { return a!=b; }
und zu erhalten, was Sie wollen, außer dass es sich eher um eine Funktion als um einen (Infix-) Operator handelt. Oder Sie könnten!=
einen anderen Operator mit dieser Bedeutung direkt verwenden oder überladen, aber dann müssen Sie natürlich sehr vorsichtig sein, um nicht versehentlich eine unbeabsichtigte Überladung mit demselben Namen zu verwenden. Und übrigens||
und&&
zurücktrue
oderfalse
, nicht1
oder0
.!
,||
und&&
ihre Argumente in einem Booleschen Kontext zu bewerten ist , nur weil diese Operatoren selten überlastet sind andere Typen zu akzeptieren; Soweit ich das beurteilen kann, erlaubt die Sprache solche Überladungen und garantiert daher keine Bewertung von Argumenten in einem booleschen Kontext.Patrick hat gute Punkte gemacht, und ich werde sie nicht wiederholen. Ich könnte jedoch vorschlagen, 'if'-Anweisungen nach Möglichkeit auf gut lesbares Englisch zu reduzieren, indem gut benannte boolesche Variablen verwendet werden. Zum Beispiel werden boolesche Operatoren verwendet, aber Sie können auch bitweise verwenden und die Bools entsprechend benennen:
bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here bool onlyBIsTrue = (b && !a); // and not need this second line if (onlyAIsTrue || onlyBIsTrue) { .. stuff .. }
Sie könnten denken, dass die Verwendung eines Booleschen Werts unnötig erscheint, aber es hilft bei zwei Hauptsachen:
EDIT: Sie haben nicht ausdrücklich gesagt, dass Sie die Bedingungen für 'if'-Anweisungen haben möchten (obwohl dies am wahrscheinlichsten erscheint), das war meine Annahme. Aber mein Vorschlag eines booleschen Zwischenwerts bleibt bestehen.
quelle
Die Verwendung bitweiser Operationen für bool hilft dabei, unnötige Verzweigungsvorhersagelogik durch den Prozessor zu sparen, die sich aus einem durch logische Operationen eingebrachten 'cmp'-Befehl ergibt.
Das Ersetzen der Logik durch bitweise Operationen (bei denen alle Operanden bool sind) generiert effizienteren Code mit demselben Ergebnis. Die Effizienz sollte idealerweise alle Kurzschlussvorteile überwiegen, die bei der Bestellung mithilfe logischer Operationen genutzt werden können.
Dies kann dazu führen, dass Code etwas unlesbar wird, obwohl der Programmierer ihn mit Gründen kommentieren sollte, warum dies getan wurde.
quelle
IIRC, viele C ++ - Compiler warnen, wenn sie versuchen, das Ergebnis einer bitweisen Operation als Bool umzuwandeln. Sie müssten eine Typumwandlung verwenden, um den Compiler glücklich zu machen.
Die Verwendung einer bitweisen Operation in einem if-Ausdruck würde der gleichen Kritik dienen, wenn auch möglicherweise nicht vom Compiler. Jeder Wert ungleich Null wird als wahr betrachtet, also ist so etwas wie "if (7 & 3)" wahr. Dieses Verhalten mag in Perl akzeptabel sein, aber C / C ++ sind sehr explizite Sprachen. Ich denke, die Spock-Augenbraue ist Due Diligence. :) Ich würde "== 0" oder "! = 0" anhängen, um klar zu machen, was Ihr Ziel war.
Aber es klingt nach einer persönlichen Präferenz. Ich würde den Code durch Flusen oder ein ähnliches Tool ausführen und sehen, ob es auch eine unkluge Strategie ist. Persönlich liest es sich wie ein Codierungsfehler.
quelle