Perché c ++ non ha && = o || = per i booleani?


126

C'è una "cosa molto brutta" che può accadere &&=e che è ||=stata usata come zucchero sintattico per bool foo = foo && bare bool foo = foo || bar?


5
Vedi questa altra domanda: stackoverflow.com/questions/2324549/… Quello su Java, ma condividendo la discendenza C, si applicano principalmente gli stessi argomenti.
Jamesdlin,

Fondamentalmente, solo c ++ non lo ha b / c in cui non lo hanno inserito - in lingue come Ruby ce l'ha. boo ...
Kache

2
Ma in Ruby, non è x ||= yapprossimativamente equivalente al C ++ x = x ? x : y;per qualsiasi tipo? In altre parole, "imposta su y se non è già impostato". Questo è molto più utile di C o C ++ x ||= y, che (escludendo il sovraccarico dell'operatore) farebbe "impostare x su (bool)yse non è già impostato". Non sono ansioso di aggiungere un altro operatore per questo, sembra un po 'debole. Basta scrivere if (!x) x = (bool)y. Ma poi, non uso boolabbastanza variabili per desiderare operatori extra che sono davvero utili solo con quel tipo.
Steve Jessop,

1
Sono sicuro che il motivo principale per cui C ++ non ha &&=o ||=è semplicemente che C non li ha. Sono ragionevolmente sicuro che il motivo per cui C non li ha è che la funzionalità non è stata considerata sufficientemente vantaggiosa.
Jonathan Leffler,

2
Inoltre, essendo ultra-pedante, la notazione bool foo = foo || bar;invocherebbe un comportamento indefinito perché foonon è inizializzata prima della valutazione di foo || bar. Ovviamente, questo dovrebbe essere qualcosa di simile bool foo = …initialization…; …; foo = foo || bar;e la domanda rimane valida.
Jonathan Leffler,

Risposte:


73

A boolpuò essere solo trueo falsein C ++. Come tale, usare &=ed |=è relativamente sicuro (anche se non mi piace particolarmente la notazione). È vero, eseguiranno operazioni a bit anziché operazioni logiche (e quindi non cortocircuiteranno) ma queste operazioni a bit seguono una mappatura ben definita, che è effettivamente equivalente alle operazioni logiche, purché entrambi gli operandi siano di tipobool . 1

Contrariamente a quanto altre persone hanno detto qui, a boolin C ++ non deve mai avere un valore diverso come 2. Quando si assegna quel valore a bool, verrà convertito truesecondo lo standard.

L'unico modo per ottenere un valore non valido in a boolè utilizzando i reinterpret_castpuntatori:

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

Ma poiché questo codice comporta comunque un comportamento indefinito, possiamo tranquillamente ignorare questo potenziale problema nel conformare il codice C ++.


1 Certamente questo è un avvertimento piuttosto grande, come dimostra il commento di Angew:

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

Il motivo è che b & 2esegue la promozione di numeri interi in modo che l'espressione sia quindi equivalente a static_cast<int>(b) & 2, che risulta in 0, che viene quindi riconvertita in a bool. Quindi è vero che l'esistenza di an operator &&=migliorerebbe la sicurezza dei tipi.


4
Ma && e || gli operatori lavoreranno su tutto ciò che converte in bool, non solo bool.
dan04

13
Non fanno la stessa cosa, nemmeno sui bool. ||e &&scorciatoia, ovvero il secondo argomento non è operando se il primo operando è true(rispettivamente falseper &&). |, &, |=E &=sempre valutare entrambi gli operandi.
Niki,

4
questo non risponde alla domanda sul perché && = e || = non sono operatori c ++.
Grazie,

9
È non sicuro da usare &=per un lato sinistro di tipo bool, perché è perfettamente possibile per il lato destro sia di tipo diverso bool(ad esempio islowero un'altra funzione stdlib C che restituisce nulli per valore effettivo). Se avessimo l'ipotetico &&=, probabilmente costringerebbe il lato destro a convertirsi bool, cosa &=che non accade. In altre parole, bool b = true; b &= 2;risulta in b == false.
Angew non è più orgoglioso di SO

2
@Antonio Folla dura. 😝
Konrad Rudolph,

44

&&e &hanno una semantica diversa: &&non valuterà il secondo operando se è il primo operando false. cioè qualcosa di simile

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

è sicuro, ma

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

non lo è, sebbene entrambi gli operandi siano di tipo bool.

Lo stesso vale per &=e |=:

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

si comporterà in modo diverso rispetto a:

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

35
motivo in più per avere && = secondo me. = P
Kache

4
Questa non è davvero una risposta però.
Catskul,

27

Risposta breve

Tutti gli operatori +=, -=, *=, /=, &=, |=... sono l'aritmetica e forniscono stessa aspettativa:

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

Tuttavia, gli operatori &&=e ||=sarebbero logici e questi operatori potrebbero essere soggetti a errori perché molti sviluppatori si aspetterebbero di foo()essere sempre chiamati 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
  • Dobbiamo davvero rendere C / C ++ ancora più complesso per ottenere una scorciatoia x = x && foo()?

  • Vogliamo davvero offuscare di più l'affermazione criptica x = x && foo()?
    O vogliamo scrivere un codice significativo come if (x) x = foo();?


Risposta lunga

Esempio per &&=

Se l' &&=operatore era disponibile, questo codice:

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

è equivalente a:

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

Questo primo codice è soggetto a errori perché molti sviluppatori pensano che f2()sia sempre chiamato qualunque sia il f1()valore restituito. È come scrivere bool ok = f1() && f2();dove f2()viene chiamato solo quando f1()ritorna true.

  • Se lo sviluppatore in realtà vuole f2()essere chiamato solo quando f1()ritorna true, quindi il secondo codice sopra è meno soggetto a errori.
  • Altrimenti (lo sviluppatore vuole f2()essere sempre chiamato), &=è sufficiente:

Esempio per &=

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

Inoltre, è più facile per il compilatore ottimizzare questo codice sopra rispetto a quello sotto:

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

Confronta &&e&

Potremmo chiederci se gli operatori &&e &dare lo stesso risultato quando applicati ai boolvalori?

Controlliamo utilizzando il seguente codice C ++:

#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);
}

Produzione:

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
======================

Conclusione

Pertanto possiamo sostituire &&da &per boolvalori ;-)
Quindi, l'uso migliore &=, invece di &&=.
Possiamo considerare &&=inutili i booleani.

Lo stesso per ||=

l'operatore |=è anche meno soggetto ad errori di||=

Se uno sviluppatore vuole f2() essere chiamato solo quando f1()ritorna false, anziché:

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 ...

Consiglio la seguente alternativa più comprensibile:

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)

o se preferisci tutto in uno stile di linea :

// 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();

13
E se volessi davvero questo comportamento? Che l'espressione della mano destra non viene eseguita se l'espressione della mano sinistra è sbagliata. È fastidioso scrivere le variabili due volte, comesuccess = success && DoImportantStuff()
Niklas R

1
Il mio consiglio è di scrivere if(success) success = DoImportantStuff(). Se la dichiarazione success &&= DoImportantStuff()fosse consentita, molti sviluppatori penserebbero che DoImportantStuff()venga sempre chiamato qualunque sia il valore di success. Spero che questo risponda a ciò che ti chiedi ... Ho anche migliorato molte parti della mia risposta. Per favore, dimmi se la mia risposta è più comprensibile ora? (a proposito del tuo commento) Saluti, ci vediamo ;-)
olibre

9
"Se la dichiarazione success &&= DoImportantStuff()fosse consentita, molti sviluppatori penserebbero che DoImportantStuff()venga sempre chiamato qualunque sia il valore del successo." Puoi dirlo if (success && DoImportantStuff())però. Finché ricordano la logica alla base della sintassi if, non dovrebbero avere problemi &&=.
Pilkch,

2
Non vedo come la gente possa presumere che f1()valuti sempre ok &&= f(1) ma non presuma che valuti sempre ok = ok && f(1). Mi sembra altrettanto probabile.
einpoklum,

1
In realtà mi aspetto v1 += e2di essere l'equivalente sintattico dello zucchero v1 = v1 + e1per la variabile v1 e l'espressione e2. Solo una notazione abbreviata, tutto qui.
einpoklum,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.