¿Hay algo "muy malo" que pueda suceder &&=
y ||=
se haya utilizado como azúcar sintáctica para bool foo = foo && bar
y bool foo = foo || bar
?
fuente
¿Hay algo "muy malo" que pueda suceder &&=
y ||=
se haya utilizado como azúcar sintáctica para bool foo = foo && bar
y bool foo = foo || bar
?
A bool
solo puede ser true
o false
en 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 bool
en C ++ nunca debe tener un valor diferente como 2
. Al asignar ese valor a a bool
, se convertirá true
según el estándar.
La única forma de obtener un valor no válido en a bool
es usar reinterpret_cast
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 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 & 2
realiza 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.
||
y &&
acceso directo, es decir, el segundo argumento no es operando si el primer operando es true
(resp. false
para &&
). |
, &
, |=
Y &=
siempre evaluar los dos operandos.
&=
para un lado izquierdo de tipo bool
, porque es perfectamente posible para el lado derecho para ser de tipo distinto bool
(como islower
u 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
.
&&
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();
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();
?
&&=
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
.
f2()
ser llamado solo cuando f1()
regrese true
, por lo tanto, el segundo código anterior es menos propenso a errores.f2()
ser llamado siempre), &=
es suficiente:&=
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
&&
y&
Podemos preguntarnos si los operadores &&
y &
dar el mismo resultado cuando se aplica a los bool
valores?
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
======================
Por lo tanto SÍ se puede sustituir &&
por &
de bool
valores ;-)
Así que utilice mejor &=
en lugar de &&=
.
Podemos considerarlo &&=
inútil para los booleanos.
||=
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();
success = success && DoImportantStuff()
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 ;-)
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 &&=
.
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.
v1 += e2
ser el equivalente de azúcar sintáctico de la v1 = v1 + e1
variable v1 y la expresión e2. Solo una notación abreviada, eso es todo.
x ||= y
má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)y
menos que ya esté configurado". No estoy ansioso por agregar otro operador para eso, parece un poco débil. Solo escribeif (!x) x = (bool)y
. Pero, en realidad, no usobool
suficientes variables como para querer operadores adicionales que solo sean realmente útiles con ese tipo.&&=
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.bool foo = foo || bar;
invocaría un comportamiento indefinido porquefoo
no se inicializa antes de la evaluación defoo || bar
. Por supuesto, esto pretende ser algo asíbool foo = …initialization…; …; foo = foo || bar;
y la pregunta entonces es válida.