bitwise - operator[] c++
¿Por qué c++ no tiene &&=o ||=para booleanos? (3)
¿Hay algo "muy malo" que pueda suceder? && = y || = se usaron como azúcar sintáctico para bool foo = foo && bar
y bool foo = foo || bar
bool foo = foo || bar
?
Respuesta corta
Todos los operadores +=
, -=
, *=
, /=
, &=
, |=
... son aritméticos y brindan la misma expectativa:
x &= foo() // We expect foo() be called whatever the value of x
Sin embargo, los operadores &&=
y ||=
serían lógicos, y estos operadores podrían ser propensos a errores porque muchos desarrolladores esperarían que foo()
siempre se llamara 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 que C / C ++ sea 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 un código significativo comoif (x) x = foo();
?
Respuesta larga
Ejemplo para &&=
Si &&=
operator 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 debido a que muchos desarrolladores pensarían que f2()
siempre se llama sea cual sea el valor devuelto f1()
. Es como escribir bool ok = f1() && f2();
donde f2()
se invoca solo cuando f1()
devuelve true
.
- Si el desarrollador realmente desea que se llame a
f2()
solo cuandof1()
devuelvetrue
, por lo tanto, el segundo código anterior es menos propenso a errores. - De lo contrario (el desarrollador quiere que siempre se llame a
f2()
),&=
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 &
dan el mismo resultado cuando se aplican a los valores de bool
?
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, SÍ , podemos reemplazar &&
por &
para valores bool
;-)
Así que es mejor usar &=
lugar de &&=
.
Podemos considerar &&=
como inútil para los booleanos.
Lo mismo para ||=
operator
|=
también es menos propenso a errores que||=
Si un desarrollador quiere que f2()
se invoque solo cuando f1()
devuelve 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 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();
Un bool
solo puede ser true
o false
en C ++. Como tal, usar &=
y |=
es perfectamente 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 provocarán un cortocircuito), 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 tipo bool
. 1
Contrariamente a lo que otras personas han dicho aquí, un 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 no válido en un bool
es mediante el uso de reinterpret_cast
en los 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`.
La razón es que b & 2
realiza la promoción de enteros de modo que la expresión es equivalente a static_cast<int>(b) & 2
, que da como resultado 0
, que luego se convierte nuevamente en un bool
. Entonces, es cierto que la existencia de un operator &&=
mejoraría la seguridad del tipo.
&&
y &
tienen 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();