comportamiento de bool con los operadores no booleanas
-
18-09-2019 - |
Pregunta
Lo que realmente quiero es un || = operador.
old_value = old_value || possible_new_value;
old_value ||= possible_new_value;
La segunda línea es un error de compilación (c ++ no tiene || = operador).
¿Cuáles son mis otras opciones?
old_value += possible_new_value;
old_value |= possible_new_value;
Mientras estoy en el tema ¿cómo se comportan bool con otros operadores booleanos no?
-
-=
&
&=
...
Puedo verificar estos empíricamente, pero estoy más interesado en lo que dice la norma.
Solución
Según 4,7 (conversiones integral), párrafo 4, "Si el tipo de destino es bool
, ver 4.12. Si el tipo de fuente es bool
, el valor false
se convierte en cero y el true
valor se convierte a uno". En 4.12, "An rvalue de la aritmética, enumeración, puntero, o puntero a tipo de miembro se puede convertir en un valor p de tipo bool
un valor cero, el valor de puntero nulo, o el valor puntero miembro nulo se convierte en false
;. Cualquier otro valor se convertido a true
".
En un contexto en operandos bool
no están permitidos pero operandos integrales son, la bool
se convertirá en un tipo integral. Cuando el resultado entero se almacena en una variable bool
, se convierte en bool
.
Por lo tanto, usted será capaz de utilizar + y * como booleano OR y AND, y se puede usar | y también. Usted no puede salirse con la mezcla de ellos, como (+ Bool1 Bool2) y bool3 rendirá false
si las tres variables son true
. ((1 + 1) y 1 es 2 y 1, que es 0, o falsa.)
Recuerde que | y || no funcionan de forma idéntica incluso aquí. | evaluará ambos lados, y luego evaluar el nivel de bits o. || evaluará el primer operando, entonces sólo si era falsa evaluará el segundo.
No voy a discutir las cuestiones estilísticas aquí, pero si lo hiciera nada por el estilo estaría seguro de hacer comentarios para que la gente sabía lo que estaba haciendo y por qué.
Otros consejos
El sayeth estándar:
4.5-4 "Promociones Integrales"
Un valor p de tipo bool puede ser convertido a un valor p de tipo int, con falso y verdadero ser cero convirtiéndose en uno.
5.17-7 "Operadores de asignación"
El comportamiento de una expresión de la formar E1 = E2 op es equivalente a E1 = E1 op E2 E1 excepto que se evalúa sólo una vez. En + = y - =, E1 deberá o bien tienen tipo aritmético o ser una puntero a una posiblemente cvqualified completamente definido tipo de objeto. En todo otros casos, E1 tendrán aritmética tipo.
4,12-1 "booleanas" Conversiones
Un valor p de la aritmética, la enumeración, puntero, o puntero a tipo de miembro puede ser convertido a un valor p de tipo bool. Un valor cero, puntero nulo valor o valor de puntero miembro nula es convertido a false; cualquier otro valor es convertido en true.
Así que esto significa que
b1 += b2
Donde b1 y b2 son boolean será equivalente a
b1 = b1 + b2
Y b1 y b2 se promoverá a 0/1 enteros, y luego se convierte de nuevo a booleano en la regla que otra cosa que no sea 0 es verdadera.
Así es la tabla de verdad
true false true true true false true false
so + = de hecho funciona como || = de acuerdo con la norma. Sin embargo, esto probablemente va a ser confuso otros programadores, lo que evitaría que sigue.
Podría usted sólo tiene que utilizar el operador ternario?
old_value = !old_value ? possible_new_value : old_value;
No utilice |=
y &=
con Bools. Pueden trabajar la mayor parte del tiempo, pero sigue siendo malo. Por lo general, el tipo bool es sólo un int glorificado o carbón. En el código anterior que he trabajado con, BOOL es sólo typedef'd a int o carbón. En estos casos, se puede obtener la respuesta equivocada si se han manipulado de alguna manera los bits (Por ejemplo, 1&2
es 0 (falso)). Y no estoy seguro, pero creo que el resultado de operadores de bits va a ser un entero, incluso para Bools.
if (!old_value)
old_value = possible_new_value;
Este es un equivalente directo de la condición original. Podría generar código más simple ya que no siempre va a asignar a old_value
-. Pero yo no esperaría que la diferencia de rendimiento que sea fácil de medir en un programa grande
Una diferencia es que los operadores lógicos tales como ||
garantizar el orden de evaluación y proporcionar un cortocircuito, donde los operadores bit a bit y aritméticas no lo hacen.
Creo que el compilador tratará los operadores no lógicos mediante la conversión de los Bools a valores numéricos (0, 1) aplicando el operador y la conversión hacia atrás. Estas conversiones se definen estrictamente por la norma, por ejemplo:.
Un valor p de la aritmética, enumeración, puntero, o puntero a tipo de miembro se puede convertir en un valor p de tipo bool. Un valor cero, el valor de puntero nulo, o el valor de puntero nulo miembro se convierte en falsa cualquier otro valor se convierte en verdadera.
Nasty macro hackery:
#define UPDATE(x) x = x
UPDATE(old_value) || possible_new_value;
Pero no recomiendo esto en absoluto. Las macros de este tipo son una muy mala idea por varias razones.
Una función más sano, pero sin el cortocircuitos:
bool set_or(bool &old, bool value) {
return (old = old || value);
}
...
bool v = false;
set_or(v, true);
Creo que la norma define explícitamente verdadero y falso como 1 y 0, por lo que puede utilizar con seguridad operadores de bits en los valores bool
. Otros tipos que pueden ser tratadas de manera implícita como Bools en otro contexto se deben convertir de forma explícita para que esto funcione de forma fiable.
He visto el compilador de Microsoft genera un aviso fea cada vez que lo haga, ya que cree que hay un peligro en convertir implícitamente el resultado int volver a bool.