Verwenden von bitweisen Operatoren für Boolesche Werte in C ++

82

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?

Jay Conrod
quelle
Wenn Sie seit dem Stellen dieser Frage ausreichend viel über C ++ gelernt haben, sollten Sie zurückkommen und die aktuelle Antwort nicht akzeptieren und die Antwort von Patrick Johnmeyer akzeptieren, um den Kurzschlussunterschied richtig zu erklären.
Codetaku
Allgemeine Bemerkung, weil die Leute fragen, ob dies eine gute Idee ist. Wenn Sie sich für die Filialabdeckung von Komponententests interessieren, ist es wünschenswert, Filialen zu reduzieren. Hierfür sind bitweise Operatoren hilfreich.
QZNC

Antworten:

59

||und &&sind boolesche Operatoren und die eingebauten Operatoren geben garantiert entweder trueoder zurück false. 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 boolist Typ nur gewährleistet , entweder eine sein trueoder ein false(die implizit bzw. konvertieren 1und 0), 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 einfach b = b && xund fertig.

Patrick
quelle
Kein logischer xor-Operator ^^ in C ++. Für beide Argumente gilt als bools! = Dasselbe, kann aber gefährlich sein, wenn eines der Argumente kein bool ist (wie es für ein logisches xor ^ wäre). Nicht sicher, wie dies akzeptiert wurde ...
Greg Rogers
1
Rückblickend war das irgendwie dumm. Wie auch immer, änderte das Beispiel in ein &&, um das Problem zu beheben. Ich schwöre, ich habe ^^ irgendwann in der Vergangenheit benutzt, aber das muss nicht der Fall gewesen sein.
Patrick
C hat einen Bool-Typ, der seit 10 Jahren nur noch 0 oder 1 sein kann. _Bool
Johannes Schaub - litb
16
Sie sind auch für Bools immer noch nicht dasselbe, weil sie nicht kurzschließen
Aarononman
3
Ja, ich bin der festen Überzeugung, dass dies nicht die akzeptierte Antwort sein sollte, bis Sie "Wenn die Domäne der Nummern, mit denen Sie arbeiten, nur 1 und 0 sind, sind sie genau gleich" in "Wenn die Domäne der Nummern, mit denen Sie arbeiten, nur 1 ist" ändern und 0 ist der einzige Unterschied, dass die bitweisen Operatoren nicht kurzschließen ". Die frühere Aussage ist völlig falsch, daher habe ich diese Antwort inzwischen abgelehnt, da sie diesen Satz enthält.
Codetaku
32

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 falseund true(oder 0und 1als 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()und b()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.

Patrick Johnmeyer
quelle
Kein wirklich fairer Vergleich, wenn Sie in einem Fall zu Bool und in dem anderen Fall zu Bool gewirkt haben ... In beiden Fällen ist die Besetzung zu Bool das, was es funktionieren lässt und warum es zerbrechlich ist (weil Sie sich daran erinnern müssen).
Greg Rogers
Die Erklärung des Kurzschlusses ist, warum dies unbedingt die akzeptierte Antwort sein sollte; Patricks Antwort (ähm ... der andere Patrick, der gerade Patrick heißt) ist völlig falsch zu sagen: "Wenn die Domäne der Zahlen, mit denen Sie arbeiten, nur 1 und 0 ist, dann sind sie genau gleich"
codetaku
13

Meiner Ansicht nach

a != b

ist was du willst

Mark Borgerding
quelle
1
Dies ist wahr, +1. Es gibt jedoch keine zuweisende Version dieses Operators ( !==sozusagen). Wenn Sie also das XOR einer Folge von boolWerten berechnen, müssen Sie acc = 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.
Marc van Leeuwen
10

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.

Kokos
quelle
3
Ja, dies sollte die beste Antwort sein. Folgen Sie dem Prinzip der geringsten Überraschung. Code, der ungewöhnliche Dinge tut, ist schwerer zu lesen und zu verstehen, und Code wird weitaus häufiger gelesen als geschrieben. Verwenden Sie solche niedlichen Tricks nicht.
Eloff
1
Ich bin hier, weil mein Kollege den bitweisen Operator "und" für bool verwendet hat. Und jetzt verbringe ich meine Zeit damit zu verstehen, ob dies richtig ist oder nicht. Wenn er das nicht tun würde, würde ich jetzt etwas Nützlicheres tun -_-. Wenn Sie die Zeit Ihrer Kollegen schätzen, verwenden Sie bitte keine bitweisen Operatoren für bool!
anton_rh
8

Nachteile der Bitlevel-Operatoren.

Du fragst:

„Gibt es einen Grund , nicht die Bit - Operatoren zu verwenden &, |und ^für‚Bool‘Werte in C ++? ”

Ja, die logischen Operatoren , die die integrierte High Level Booleschen Operatoren sind !, &&und ||bietet folgende Vorteile:

  • Garantierte Umwandlung von Argumenten in bool, dh in 0und 1Ordnungswert.

  • 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, andund orauch wenn ich sie nicht selbst benutze.
    Als Leser Antimony Notizen in einem Kommentar auch die bitlevel Operatoren alternative Token, nämlich bitand, bitor, xorund compl, aber meiner Meinung nach sind diese weniger lesbar als and, orund not.

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 & 20, während 1 && 2true. Auch ^bitweise exklusiv oder kann sich auf diese Weise schlecht benehmen. Als boolesche Werte betrachtet, sind 1 und 2 gleich true, 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,

"Manchmal stoße ich auf Situationen, in denen genau eine von zwei Bedingungen erfüllt sein soll (XOR), also wirf ich den Operator ^ einfach in einen bedingten Ausdruck."

Nun, die bitweisen Operatoren haben eine höhere Priorität als die logischen Operatoren. Dies bedeutet insbesondere, dass in einem gemischten Ausdruck wie

a && b ^ c

Sie erhalten das vielleicht unerwartete Ergebnis a && (b ^ c).

Schreiben Sie stattdessen einfach

(a && b) != c

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 ^ cals das ist nicht ein Ausdruck, der „entweder sagt a, boder cist wahr“. Stattdessen heißt es : „Eine ungerade Anzahl von a, bund cwahr sind“, was 1 von ihnen sein könnte oder alle 3 ...

Um auszudrücken , den allgemeinen entweder / oder , wenn a, bund csind vom Typ bool, nur Schreib

(a + b + c) == 1

oder boolkonvertieren Sie sie mit Nichtargumenten in bool:

(!!a + !!b + !!c) == 1


Verwenden &=, um boolesche Ergebnisse zu akkumulieren.

Sie arbeiten weiter aus,

"Ich muss manchmal auch Boolesche Werte akkumulieren &=und |=?kann sehr nützlich sein."

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:

wahr
falsch

Der Grund für den Unterschied in den Ergebnissen ist, dass der Bitlevel &=keine Konvertierung in boolsein Argument auf der rechten Seite bietet .

Welches dieser Ergebnisse wünschen Sie sich für Ihre Verwendung &=?

Wenn erstere, truedefinieren 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.

Prost und hth. - Alf
quelle
bitweise Operatoren haben auch Textäquivalente
Antimon
@Antimony: Ich verstand nicht , diese Bemerkung auf den ersten , aber ja, haben die bitlevel Operationen alternative Token bitand, bitor, xorund compl. Ich denke, deshalb habe ich die Qualifikation "lesbar" verwendet. Natürlich ist die Lesbarkeit von zB compl 42subjektiv. ;-)
Prost und hth. - Alf
Soweit ich logische Operatoren sagen kann , kann mit anderen Argumenttypen überlastet werden (obwohl sie in der Regel nicht sind), so der erste Punkt „garantiert Umwandlung von Argumenten bool“ nur eine Folge der Konvention ist, keine Garantie , dass die Sprache C ++ gibt tatsächlich Sie.
Marc van Leeuwen
@MarcvanLeeuwen: Möglicherweise hat Ihr visuelles Subsystem "die integrierten booleschen Operatoren auf hoher Ebene !, && und ||" nicht bemerkt. Sie haben Recht, dass diese Operatoren überladen werden können, und dann besteht ein Hauptproblem darin, dass sich die Semantik subtil ändert und keine Kurzschlussauswertung mehr erfolgt. Dies ist jedoch ein Problem bei Überlastungen, nicht bei den integrierten Operatoren.
Prost und hth. - Alf
Das ist richtig (das habe ich vermisst). Aber dann ist die Aussage immer noch irreführend, weil Sie nur sagen, dass diese Operatoren boolesche Argumente verwenden; Wenn (und nur wenn) die integrierten Versionen der Operatoren ausgewählt werden, hat der Compiler eine Konvertierung der Argumente eingefügt. In ähnlicher Weise +garantiert der eingebaute (übergeordnete) Ganzzahl-Additionsoperator ohne Vorzeichen die Konvertierung seiner Argumente in unsigned, aber dies verhindert nicht, dass unsigned int n=-1; n+=3.14;die double(oder ist es float?) Additionsoperation tatsächlich verwendet wird . (Vergleichen Sie Ihr &=Beispiel.)
Marc van Leeuwen
3

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 nicht bool"Booleschen" Typen gilt jedoch im Vergleich 1 & 2zu 1 && 2. Ein klassisches Beispiel hierfür ist die Windows- GetMessage()Funktion, die einen Drei-Status zurückgibt BOOL: ungleich Null 0, 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.

bk1e
quelle
6
Ein ^^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.
Craig McQueen
@CraigMcQueen. Es ist einfach zu definieren 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ück trueoder false, nicht 1oder 0.
Marc van Leeuwen
Und es scheint , dass die Tatsache , dass !, ||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.
Marc van Leeuwen
2

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:

  • Ihr Code ist leichter zu verstehen, da der Zwischen-Boolesche Wert für die 'if'-Bedingung die Absicht der Bedingung expliziter macht.
  • Wenn Sie nicht standardmäßigen oder unerwarteten Code verwenden, z. B. bitweise Operatoren für boolesche Werte, können die Benutzer viel einfacher erkennen, warum Sie dies getan haben.

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.

Genix
quelle
1

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.

Anoop Menon
quelle
0

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.

Spoulson
quelle
Ihr Beitrag hat mich motiviert, dies zu testen, und gcc hat nicht gewarnt! Schade, weil ich diese Warnung verwenden wollte, um zu rechtfertigen, dass ich & = laufen lasse, um Bool-Ergebnisse zu sammeln, und darauf vertraue, dass andere die Warnung sehen würden, wenn sie später meinen Test ändern. Mein Code würde für gerade ganze Zahlen fehlschlagen, da sie 0 / false ergeben - ohne Warnungen! Zeit zum Refactor ...
Salbei