Pregunta ¿Por qué c ++ no tiene && = o || = para booleanos?


Hay un "algo muy malo" eso puede suceder && = y || = se usaron como azúcar sintáctico para bool foo = foo && bar y bool foo = foo || bar?


88
2018-03-21 19:29


origen


Respuestas:


UN bool solo puede ser true o false en C ++. Como tal, utilizando &= y |= es perfectamente seguro (a pesar de que no me gusta particularmente la notación). Es cierto que realizarán operaciones de bits en lugar de operaciones lógicas (y por lo tanto no provocarán un cortocircuito), pero estas operaciones de bits siguen un mapeo bien definido, que es efectivamente equivalente a las operaciones lógicas, Mientras ambos operandos son de tipo bool.1

Contrario a lo que otras personas han dicho aquí, una bool en C ++ nunca debe tener un valor diferente, como 2. Al asignar ese valor a un bool, se convertirá en true según el estándar.

La única forma de obtener un valor inválido en un bool es mediante el uso reinterpret_cast en punteros:

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

Pero dado que este código da como resultado un comportamiento indefinido de todos modos, podemos ignorar con seguridad este posible problema al conformar el código C ++.


1 Es cierto que esta es una advertencia bastante grande como ilustra el comentario de Angew:

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

El motivo es que b & 2 realiza la promoción de enteros de modo que la expresión sea equivalente a static_cast<int>(b) & 2, lo que resulta en 0, que luego se convierte de nuevo en una bool. Entonces, es cierto que la existencia de un operator &&= mejoraría la seguridad del tipo.


57
2018-03-21 20:02



&& y & tener una semántica diferente && no evaluará el segundo operando si el primer operando es false. es decir, algo así como

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

es seguro, pero

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

no lo es, aunque ambos operandos son de tipo bool.

Lo mismo es cierto para &= y |=:

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

se comportará de manera diferente a:

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

39
2018-03-21 20:13



Respuesta corta

Todos los operadores +=, -=, *=, /=, &=, |=... son aritméticas y proporcionan la misma expectativa:

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

Sin embargo, los operadores &&= y ||= sería lógico, y estos operadores podrían ser propensos a errores porque muchos desarrolladores esperarían foo() ser llamado siempre en 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
  • ¿Realmente necesitamos hacer C / C ++ aún más complejo para obtener un atajo para x = x && foo()?

  • ¿Realmente queremos ofuscar más la declaración críptica x = x && foo()?
    O queremos escribir código significativo como if (x) x = foo();?


Respuesta larga

Ejemplo para &&=

Si &&= el operador estaba disponible, luego este código:

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

es equivalente a:

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

Este primer código es propenso a errores porque muchos desarrolladores pensarían f2() siempre se llama lo que sea f1() valor devuelto Es como escribir bool ok = f1() && f2(); dónde f2() se llama solo cuando f1() devoluciones true.

  • Si el desarrollador realmente quiere f2() ser llamado solo cuando f1() devoluciones true, por lo tanto, el segundo código anterior es menos propenso a errores.
  • Else (el desarrollador quiere f2() ser llamado siempre), &= es suficiente:

Ejemplo para &=

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

Además, es más fácil para el compilador optimizar este código anterior que el siguiente:

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

Comparar && y &

Podemos preguntarnos si los operadores && y &dar el mismo resultado cuando se aplica en bool ¿valores?

Vamos a verificar usando el siguiente código de 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);
}

Salida:

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

Conclusión

Por lo tanto  podemos reemplazar && por & para bool valores ;-)
Así que mejor uso &= en lugar de &&=.
Podemos considerar &&= como inútil para los booleanos.

Igual por ||=

operador |= también es menos propenso a errores que ||=

Si un desarrollador quiere f2() ser llamado solo cuando f1() devoluciones false, en lugar de:

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

Aconsejo la siguiente alternativa más comprensible:

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 si prefieres todo en una línea estilo:

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

19
2018-03-29 15:28