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

126

¿Hay algo "muy malo" que pueda suceder &&=y ||=se haya utilizado como azúcar sintáctica para bool foo = foo && bary bool foo = foo || bar?

Kache
fuente
55
Vea esta otra pregunta: stackoverflow.com/questions/2324549/... Esa es sobre Java, pero compartiendo el linaje C, se aplican principalmente los mismos argumentos.
jamesdlin
Básicamente, solo c ++ no lo tiene b / c en el que no lo pusieron, lenguajes como Ruby lo tiene. boo ...
Kache
2
Pero en Ruby, ¿no es x ||= ymás o menos equivalente a C ++ x = x ? x : y;para cualquier tipo? En otras palabras, "establecer en y si aún no está configurado". Eso es considerablemente más útil que C o C ++ x ||= y, que (salvo sobrecarga del operador) haría "establecer x a (bool)ymenos que ya esté configurado". No estoy ansioso por agregar otro operador para eso, parece un poco débil. Solo escribe if (!x) x = (bool)y. Pero, en realidad, no uso boolsuficientes variables como para querer operadores adicionales que solo sean realmente útiles con ese tipo.
Steve Jessop
1
Estoy seguro de que la razón principal por la que C ++ no tiene &&=o ||=es simplemente que C no los tiene. Estoy razonablemente seguro de que la razón por la que C no los tiene es que la funcionalidad no se consideró lo suficientemente beneficiosa.
Jonathan Leffler
2
Además, al ser ultra pedante, la notación bool foo = foo || bar;invocaría un comportamiento indefinido porque foono se inicializa antes de la evaluación de foo || bar. Por supuesto, esto pretende ser algo así bool foo = …initialization…; …; foo = foo || bar;y la pregunta entonces es válida.
Jonathan Leffler

Respuestas:

73

A boolsolo puede ser trueo falseen C ++. Como tal, usar &=y |=es relativamente seguro (aunque 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 cortocircuitarán), pero estas operaciones de bits siguen una asignación bien definida, que es efectivamente equivalente a las operaciones lógicas, siempre que ambos operandos sean de tipobool . 1

Al contrario de lo que otras personas han dicho aquí, un boolen C ++ nunca debe tener un valor diferente como 2. Al asignar ese valor a a bool, se convertirá truesegún el estándar.

La única forma de obtener un valor no válido en a booles usar reinterpret_castpunteros:

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 este posible problema al conformar el código C ++.


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

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

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

Konrad Rudolph
fuente
44
Pero el && y || los operadores trabajarán en cualquier cosa que se convierta en bool, no solo bool.
dan04
13
No hacen lo mismo, incluso en los bools. ||y &&acceso directo, es decir, el segundo argumento no es operando si el primer operando es true(resp. falsepara &&). |, &, |=Y &=siempre evaluar los dos operandos.
Niki
44
Esto no responde a la pregunta de por qué && = y || = no son operadores de C ++.
thang
9
Es no seguro de usar &=para un lado izquierdo de tipo bool, porque es perfectamente posible para el lado derecho para ser de tipo distinto bool(como isloweru otra función stdlib C que devuelve distinto de cero para cierto valor). Si tuviéramos lo hipotético &&=, probablemente forzaría la conversión del lado derecho bool, que &=no es así. En otras palabras, bool b = true; b &= 2;resulta en b == false.
Angew ya no está orgulloso de SO
2
@Antonio Tough multitud. 😝
Konrad Rudolph
44

&&y &tienen una semántica diferente: &&no evaluará el segundo operando si el primer operando es false. es decir, algo 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();
Niki
fuente
35
Razón de más para tener && = en mi opinión. = P
Kache
44
Sin embargo, esto no es realmente una respuesta.
Catskul
27

Respuesta corta

Todos los operadores +=, -=, *=, /=, &=, |=... son la aritmética y proporcionan 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 propenso a errores debido a que muchos desarrolladores se pueden esperar foo()ser siempre llamaron 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 que C / C ++ sea aún más complejo para obtener un atajo 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, entonces 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 que f2()siempre se llama cualquiera que sea el f1()valor devuelto. Es como escribir bool ok = f1() && f2();donde f2()se llama solo cuando f1()regresa true.

  • Si el desarrollador realmente quiere f2()ser llamado solo cuando f1()regrese true, por lo tanto, el segundo código anterior es menos propenso a errores.
  • De lo contrario (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

Compara &&y&

Podemos preguntarnos si los operadores &&y &dar el mismo resultado cuando se aplica a los boolvalores?

Vamos a verificar usando el siguiente código 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 se puede sustituir &&por &de boolvalores ;-)
Así que utilice mejor &=en lugar de &&=.
Podemos considerarlo &&=inútil para los booleanos.

Igual por ||=

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

Si un desarrollador quiere f2()ser llamado solo cuando f1()regrese 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 prefiere todo en un estilo de línea :

// 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
fuente
13
¿Qué pasa si realmente quiero este comportamiento? Que la expresión de la mano derecha no se ejecuta si la expresión de la mano izquierda es incorrecta. Es molesto escribir las variables dos veces, comosuccess = success && DoImportantStuff()
Niklas R
1
Mi consejo es escribir if(success) success = DoImportantStuff(). Si success &&= DoImportantStuff()se permitiera la declaración , muchos desarrolladores pensarían que DoImportantStuff()siempre se llama cualquiera sea el valor de success. Espero que esto responda a lo que te preguntas ... También he mejorado muchas partes de mi respuesta. ¿Dime si mi respuesta es más comprensible ahora? (sobre el propósito de tu comentario) Saludos, nos vemos ;-)
olibre
9
"Si success &&= DoImportantStuff()se permitiera la declaración , muchos desarrolladores pensarían que DoImportantStuff()siempre se llama cualquiera que sea el valor del éxito". Sin if (success && DoImportantStuff())embargo, puedes decir eso . Mientras recuerden la lógica detrás de la sintaxis if, no deberían tener problemas &&=.
Pilkch
2
No veo cómo la gente puede suponer que f1()siempre se evalúa, ok &&= f(1) pero no asumirá que siempre se evalúa ok = ok && f(1). Me parece igual de probable.
einpoklum
1
De hecho, espero v1 += e2ser el equivalente de azúcar sintáctico de la v1 = v1 + e1variable v1 y la expresión e2. Solo una notación abreviada, eso es todo.
einpoklum