Warum hat c ++ nicht && = oder || = für Boolesche Werte?

126

Gibt es eine "sehr schlechte Sache" , die passieren kann &&=und ||=als syntaktischer Zucker für bool foo = foo && barund verwendet wurde bool foo = foo || bar?

Kache
quelle
5
Siehe diese andere Frage: stackoverflow.com/questions/2324549/… Da es sich um Java handelt, aber die C-Linie gemeinsam genutzt wird, gelten meistens dieselben Argumente.
Jamesdlin
Grundsätzlich hat nur c ++ es nicht b / c sie haben es nicht eingefügt - Sprachen wie Ruby haben es. boo ...
Kache
2
Aber ist Ruby in Ruby für keinen Typ x ||= yungefähr gleichwertig x = x ? x : y;? Mit anderen Worten, "auf y setzen, wenn nicht bereits gesetzt". Das ist wesentlich nützlicher als C oder C ++ x ||= y, das ( (bool)yaußer beim Überladen des Operators) "x setzen, wenn nicht bereits gesetzt". Ich bin nicht bestrebt, einen weiteren Operator dafür hinzuzufügen, es scheint ein bisschen schwach. Schreib einfach if (!x) x = (bool)y. Aber dann verwende ich boolVariablen nicht genug, um zusätzliche Operatoren zu wollen, die nur für diesen einen Typ wirklich nützlich sind.
Steve Jessop
1
Ich bin sicher, der Hauptgrund, warum C ++ keine hat &&=oder ||=einfach ist, dass C sie nicht hat. Ich bin mir ziemlich sicher, dass C sie nicht hat, weil die Funktionalität nicht als vorteilhaft genug angesehen wurde.
Jonathan Leffler
2
bool foo = foo || bar;Da die Notation ultra-pedantisch ist, würde sie auch undefiniertes Verhalten hervorrufen, da sie foovor der Auswertung von nicht initialisiert wird foo || bar. Natürlich soll dies so etwas sein bool foo = …initialization…; …; foo = foo || bar;und die Frage gilt dann als gültig.
Jonathan Leffler

Antworten:

73

A booldarf nur trueoder falsein C ++ sein. Daher ist die Verwendung von &=und |=relativ sicher (obwohl mir die Notation nicht besonders gefällt). Zwar führen sie eher Bitoperationen als logische Operationen aus (und schließen daher nicht kurz), aber diese Bitoperationen folgen einer genau definierten Zuordnung, die den logischen Operationen effektiv entspricht, solange beide Operanden vom Typ sindbool . 1

Im Gegensatz zu dem, was andere hier gesagt haben, booldarf a in C ++ niemals einen anderen Wert haben, wie z 2. Wenn Sie diesen Wert a zuweisen, boolwird er truegemäß Standard konvertiert .

Der einzige Weg, um einen ungültigen Wert in a zu bekommen, boolist die Verwendung von reinterpret_caston-Zeigern:

int i = 2;
bool b = *reinterpret_cast<bool*>(&i);
b |= true; // MAY yield 3 (but doesn’t on my PC!)

Da dieser Code jedoch ohnehin zu undefiniertem Verhalten führt, können wir dieses potenzielle Problem bei der Anpassung von C ++ - Code ignorieren.


1 Zugegeben, dies ist eine ziemlich große Einschränkung, wie Angews Kommentar zeigt:

bool b = true;
b &= 2; // yields `false`.

Der Grund dafür ist, dass b & 2eine ganzzahlige Heraufstufung so durchgeführt wird, dass der Ausdruck dann äquivalent zu ist static_cast<int>(b) & 2, was zu einem Ergebnis führt 0, das dann wieder in a konvertiert wird bool. Es ist also wahr, dass die Existenz eines die operator &&=Typensicherheit verbessern würde.

Konrad Rudolph
quelle
4
Aber die && und || Operatoren arbeiten an allem, was in Bool konvertiert wird , nicht nur an Bool.
dan04
13
Sie machen nicht das Gleiche, auch nicht auf Narren. ||und &&Verknüpfung, dh das zweite Argument ist kein Operand, wenn der erste Operand ist true(bzw. falsefür &&). |, &, |=Und &=bewertet immer beiden Operanden.
Niki
4
Dies beantwortet nicht die Frage, warum && = und || = keine C ++ - Operatoren sind.
Thang
9
Es ist nicht sicher, &=für eine linke Seite des Typs zu verwenden bool, da es durchaus möglich ist, dass die rechte Seite von einem anderen Typ als bool(z. B. isloweroder einer anderen C-stdlib-Funktion, die für den wahren Wert ungleich Null zurückgibt) ist. Wenn wir die Hypothese hätten &&=, würde dies wahrscheinlich die rechte Seite zwingen, zu konvertieren bool, was &=nicht der Fall ist. Mit anderen Worten, bool b = true; b &= 2;ergibt b == false.
Angew ist nicht mehr stolz auf SO
2
@ Antonio Tough Menge. 😝
Konrad Rudolph
44

&&und &haben unterschiedliche Semantik: &&wird den zweiten Operanden nicht auswerten, wenn der erste Operand ist false. dh so etwas wie

flag = (ptr != NULL) && (ptr->member > 3);

ist sicher, aber

flag = (ptr != NULL) & (ptr->member > 3);

ist nicht, obwohl beide Operanden vom Typ sind bool.

Gleiches gilt für &=und |=:

flag = CheckFileExists();
flag = flag && CheckFileReadable();
flag = flag && CheckFileContents();

verhält sich anders als:

flag = CheckFileExists();
flag &= CheckFileReadable();
flag &= CheckFileContents();
Niki
quelle
35
umso mehr Grund, meiner Meinung nach && = zu haben. = P
Kache
4
Dies ist jedoch keine wirkliche Antwort.
Catskul
27

Kurze Antwort

Alle Betreiber +=, -=, *=, /=, &=, |=... sind arithmetische und bieten gleiche Erwartung:

x &= foo()  // We expect foo() be called whatever the value of x

Operatoren &&=und ||=wären jedoch logisch, und diese Operatoren könnten fehleranfällig sein, da viele Entwickler erwarten würden foo(), immer aufgerufen zu werden x &&= foo().

bool x;
// ...
x &&= foo();           // Many developers might be confused
x = x && foo();        // Still confusing but correct
x = x ? foo() : x;     // Understandable
x = x ? foo() : false; // Understandable
if (x) x = foo();      // Obvious
  • Müssen wir C / C ++ wirklich noch komplexer machen, um eine Verknüpfung zu erhalten x = x && foo()?

  • Wollen wir die kryptische Aussage wirklich mehr verschleiern x = x && foo()?
    Oder wollen wir aussagekräftigen Code schreiben wie if (x) x = foo();?


Lange Antwort

Beispiel für &&=

Wenn der &&=Operator verfügbar war, dann dieser Code:

bool ok = true; //becomes false when at least a function returns false
ok &&= f1();
ok &&= f2(); //we may expect f2() is called whatever the f1() returned value

ist äquivalent zu:

bool ok = true;
if (ok) ok = f1();
if (ok) ok = f2(); //f2() is called only when f1() returns true

Dieser erste Code ist fehleranfällig, da viele Entwickler glauben, dass er f2()immer unabhängig vom f1()zurückgegebenen Wert aufgerufen wird. Es ist wie beim Schreiben, bool ok = f1() && f2();wo f2()nur bei der f1()Rückkehr aufgerufen wird true.

  • Wenn der Entwickler tatsächlich f2()nur bei der f1()Rückgabe aufgerufen werden möchte , ist trueder zweite Code oben weniger fehleranfällig.
  • Sonst (der Entwickler möchte f2()immer angerufen werden) &=ist ausreichend:

Beispiel für &=

bool ok = true;
ok &= f1();
ok &= f2(); //f2() always called whatever the f1() returned value

Darüber hinaus ist es für den Compiler einfacher, diesen obigen Code zu optimieren als den folgenden:

bool ok = true;
if (!f1())  ok = false;
if (!f2())  ok = false;  //f2() always called

Vergleiche &&und&

Wir fragen uns vielleicht, ob die Operatoren &&und &das gleiche Ergebnis boolliefern, wenn sie auf Werte angewendet werden.

Überprüfen wir dies mit dem folgenden C ++ - Code:

#include <iostream>

void test (int testnumber, bool a, bool b)
{
   std::cout << testnumber <<") a="<< a <<" and b="<< b <<"\n"
                "a && b = "<< (a && b)  <<"\n"
                "a &  b = "<< (a &  b)  <<"\n"
                "======================"  "\n";
}

int main ()
{
    test (1, true,  true);
    test (2, true,  false);
    test (3, false, false);
    test (4, false, true);
}

Ausgabe:

1) a=1 and b=1
a && b = 1
a &  b = 1
======================
2) a=1 and b=0
a && b = 0
a &  b = 0
======================
3) a=0 and b=0
a && b = 0
a &  b = 0
======================
4) a=0 and b=1
a && b = 0
a &  b = 0
======================

Fazit

Daher JA können wir ersetzen &&durch &für boolWerte ;-)
Also besser nutzen &=statt &&=.
Wir können &&=für Boolesche als nutzlos betrachten.

Gleiches gilt für ||=

Betreiber |=ist auch weniger fehleranfällig als||=

Wenn ein Entwickler f2()nur bei der f1()Rückgabe angerufen werden möchte false, anstatt:

bool ok = false;
ok ||= f1();
ok ||= f2(); //f2() is called only when f1() returns false
ok ||= f3(); //f3() is called only when f1() or f2() return false
ok ||= f4(); //f4() is called only when ...

Ich rate die folgende verständlichere Alternative:

bool ok = false;
if (!ok) ok = f1();
if (!ok) ok = f2();
if (!ok) ok = f3();
if (!ok) ok = f4();
// no comment required here (code is enough understandable)

oder wenn Sie alle in einem Linienstil bevorzugen :

// this comment is required to explain to developers that 
// f2() is called only when f1() returns false, and so on...
bool ok = f1() || f2() || f3() || f4();
olibre
quelle
13
Was ist, wenn ich dieses Verhalten tatsächlich möchte? Dass der Ausdruck für die rechte Hand nicht ausgeführt wird, wenn der Ausdruck für die linke Hand falsch ist. Es ist ärgerlich, die Variablen zweimal zu schreiben, wiesuccess = success && DoImportantStuff()
Niklas R
1
Mein Rat ist zu schreiben if(success) success = DoImportantStuff(). Wenn die Anweisung success &&= DoImportantStuff()erlaubt wäre, würden viele Entwickler denken, dass sie DoImportantStuff()immer unabhängig vom Wert von genannt wird success. Hoffe, dies beantwortet, was Sie sich fragen ... Ich habe auch viele Teile meiner Antwort verbessert. Bitte sagen Sie mir, ob meine Antwort jetzt verständlicher ist. (über Ihren Kommentar Zweck) Prost, wir sehen uns ;-)
olibre
9
"Wenn die Aussage success &&= DoImportantStuff()erlaubt wäre, würden viele Entwickler denken, dass sie DoImportantStuff()immer als Wert des Erfolgs bezeichnet wird." Das kann man aber sagen if (success && DoImportantStuff()). Solange sie sich an die Logik hinter der if-Syntax erinnern, sollten sie keine Probleme damit haben &&=.
Pilkch
2
Ich verstehe nicht, wie die Leute annehmen können, dass f1()immer bewertet wird, ok &&= f(1) aber nicht, dass es immer bewertet wird ok = ok && f(1). Scheint mir genauso wahrscheinlich.
Einpoklum
1
Ich erwarte tatsächlich v1 += e2, das syntaktische Zuckeräquivalent v1 = v1 + e1für die Variable v1 und den Ausdruck e2 zu sein. Nur eine Kurzschreibweise, das ist alles.
Einpoklum