¿Hay alguna razón para no usar los operadores bit a bit &, | y ^ para valores "bool" en C ++?
A veces me encuentro con situaciones en las que quiero que se cumpla exactamente una de las dos condiciones (XOR), por lo que simplemente lanzo el operador ^ a una expresión condicional. A veces también quiero que se evalúen todas las partes de una condición si el resultado es verdadero o no (en lugar de un cortocircuito), así que uso & y |. También necesito acumular valores booleanos a veces, y & = y | = pueden ser bastante útiles.
Me han levantado algunas cejas al hacer esto, pero el código sigue siendo significativo y más limpio de lo que sería de otra manera. ¿Hay alguna razón para NO usarlos para bools? ¿Hay compiladores modernos que den malos resultados para esto?
fuente
Respuestas:
||
y&&
son operadores booleanos y se garantiza que los integrados devuelvantrue
ofalse
. Nada más.|
,&
Y^
son a nivel de bits operadores. Cuando el dominio de los números en el que opera es solo 1 y 0, entonces son exactamente los mismos, pero en los casos en que sus valores booleanos no son estrictamente 1 y 0, como es el caso del lenguaje C, puede terminar con algún comportamiento no querías. Por ejemplo:BOOL two = 2; BOOL one = 1; BOOL and = two & one; //and = 0 BOOL cand = two && one; //cand = 1
En C ++, sin embargo,
bool
se garantiza que el tipo sea solo atrue
o afalse
(que se convierten implícitamente en respectivamente1
y0
), por lo que es menos preocupante desde esta postura, pero el hecho de que la gente no está acostumbrada a ver tales cosas en el código. es un buen argumento para no hacerlo. Solo dilob = b && x
y termina con eso.fuente
Dos razones principales. En resumen, considere cuidadosamente; podría haber una buena razón para ello, pero si hay algo MUY explícito en tus comentarios porque puede ser frágil y, como tú mismo dices, la gente generalmente no está acostumbrada a ver código como este.
Xor bit a bit! = Xor lógico (excepto para 0 y 1)
En primer lugar, si está operando con valores distintos de
false
ytrue
(o0
y1
, como enteros), el^
operador puede introducir un comportamiento no equivalente a un xor lógico. Por ejemplo:int one = 1; int two = 2; // bitwise xor if (one ^ two) { // executes because expression = 3 and any non-zero integer evaluates to true } // logical xor; more correctly would be coded as // if (bool(one) != bool(two)) // but spelled out to be explicit in the context of the problem if ((one && !two) || (!one && two)) { // does not execute b/c expression = ((true && false) || (false && true)) // which evaluates to false }
Crédito al usuario @Patrick por expresar esto primero.
Orden de operaciones
En segundo lugar,
|
,&
, y^
, como operadores de bits, no provoque un cortocircuito. Además, varios operadores bit a bit encadenados en una sola declaración, incluso con paréntesis explícitos, se pueden reordenar optimizando los compiladores, ya que las 3 operaciones son normalmente conmutativas. Esto es importante si importa el orden de las operaciones.En otras palabras
bool result = true; result = result && a() && b(); // will not call a() if result false, will not call b() if result or a() false
no siempre dará el mismo resultado (o estado final) que
bool result = true; result &= (a() & b()); // a() and b() both will be called, but not necessarily in that order in an // optimizing compiler
Esto es especialmente importante porque es posible que no controle los métodos
a()
yb()
, o alguien más puede venir y cambiarlos más tarde sin comprender la dependencia, y causar un error desagradable (y, a menudo, solo para la versión de lanzamiento).fuente
Yo creo que
es lo que quieres
fuente
!==
por así decirlo), por lo que si está calculando el XOR de una secuencia debool
valores, deberá escribiracc = acc!= condition(i);
en un cuerpo de bucle. El compilador probablemente pueda manejar esto tan eficientemente como si!==
existiera, pero algunos pueden encontrar que no se ve bien y prefieren la alternativa de agregar los valores booleanos como enteros y luego probar si la suma es impar.Las cejas levantadas deberían decirle lo suficiente como para dejar de hacerlo. No escribe el código para el compilador, primero lo escribe para sus compañeros programadores y luego para el compilador. Incluso si los compiladores funcionan, sorprender a otras personas no es lo que desea: los operadores bit a bit son para operaciones de bit, no para bools.
¿Supongo que también comes manzanas con tenedor? Funciona pero sorprende a la gente, así que es mejor no hacerlo.
fuente
Desventajas de los operadores de nivel de bits.
Usted pregunta:
Sí, los operadores lógicos , que es la incorporada en operadores booleanos de alto nivel
!
,&&
y||
, ofrecer las siguientes ventajas:Garantizada conversión de argumentos a
bool
, es decir, a0
y1
valor ordinal.Evaluación de cortocircuito garantizada donde la evaluación de expresión se detiene tan pronto como se conoce el resultado final.
Esto se puede interpretar como una lógica de valor de árbol, con Verdadero , Falso e Indeterminado .
Equivalentes textuales legibles
not
,and
eor
incluso si no los uso yo mismo.Como lector de antimonio notas en un comentario también a los operadores bitlevel tienen fichas alternativas, a saber
bitand
,bitor
,xor
ycompl
, pero en mi opinión estos son menos legible queand
,or
ynot
.En pocas palabras, cada una de estas ventajas de los operadores de alto nivel es una desventaja de los operadores de nivel de bits.
En particular, dado que los operadores bit a bit carecen de conversión de argumentos a 0/1, se obtiene, por ejemplo ,
1 & 2
→0
, mientras que1 && 2
→true
. Además^
, bit a bit exclusivo o, puede comportarse mal de esta manera. Considerados como valores booleanos 1 y 2 son iguales, es decirtrue
, pero considerados como patrones de bits, son diferentes.Cómo expresar una lógica o una en C ++.
A continuación, proporcione algunos antecedentes para la pregunta,
Bueno, los operadores bit a bit tienen mayor precedencia que los operadores lógicos. Esto significa, en particular, que en una expresión mixta como
obtienes el resultado quizás inesperado
a && (b ^ c)
.En su lugar, escribe solo
expresando de forma más concisa lo que quieres decir.
Para el argumento múltiple o / o no hay operador de C ++ que hace el trabajo. Por ejemplo, si se escribe
a ^ b ^ c
de lo que no es una expresión que dice “ya seaa
,b
oc
es verdad”. En su lugar, dice, “un número impar dea
,b
yc
son verdaderos”, que podría ser 1 de ellos o los 3 ...Para expresar el general o cuando
a
,b
yc
son de tipobool
, simplemente escriba(a + b + c) == 1
o, sin
bool
argumentos, conviértalos abool
:(!!a + !!b + !!c) == 1
Utilizando
&=
para acumular resultados booleanos.Usted elabora más,
Bueno, esto corresponde a comprobar si se cumple respectivamente todas o alguna de las condiciones, y la ley de De Morgan te dice cómo pasar de una a otra. Es decir, solo necesitas uno de ellos. En principio, podría utilizarlo
*=
como&&=
operador (porque, como descubrió el bueno de George Boole, el Y lógico se puede expresar fácilmente como una multiplicación), pero creo que eso dejaría perplejos y quizás engañaría a los mantenedores del código.Considere también:
struct Bool { bool value; void operator&=( bool const v ) { value = value && v; } operator bool() const { return value; } }; #include <iostream> int main() { using namespace std; Bool a = {true}; a &= true || false; a &= 1234; cout << boolalpha << a << endl; bool b = {true}; b &= true || false; b &= 1234; cout << boolalpha << b << endl; }
Salida con Visual C ++ 11.0 y g ++ 4.7.1:
La razón de la diferencia en los resultados es que el nivel de bits
&=
no proporciona una conversión abool
su argumento del lado derecho.Entonces, ¿cuál de estos resultados desea utilizar
&=
?Si es el primero,
true
entonces defina mejor un operador (por ejemplo, como arriba) o una función nombrada, o use una conversión explícita de la expresión del lado derecho, o escriba la actualización completa.fuente
bitand
,bitor
,xor
ycompl
. Creo que por eso utilicé la calificación "legible". Por supuesto, la legibilidad de egcompl 42
es subjetiva. ;-)bool
" es solo una consecuencia de la convención, no una garantía de que el lenguaje C ++ realmente da tú.+
garantiza la conversión de sus argumentos aunsigned
, pero eso no impideunsigned int n=-1; n+=3.14;
que se utilice realmente la operación de sumadouble
(¿o sífloat
?). (Compare su&=
ejemplo.)Al contrario de la respuesta de Patrick, C ++ no tiene
^^
operador para realizar un cortocircuito o exclusivo. Si lo piensa por un segundo, tener un^^
operador no tendría sentido de todos modos: con exclusivo o, el resultado siempre depende de ambos operandos. Sin embargo, la advertencia de Patrick sobre losbool
tipos no "booleanos" es igualmente válida cuando se compara1 & 2
con1 && 2
. Un ejemplo clásico de esto es laGetMessage()
función de Windows , que devuelve un tri-estadoBOOL
: distinto de cero0
, o-1
.Utilizando
&
lugar de&&
y en|
lugar de||
no es un error tipográfico poco común, por lo que si lo está haciendo deliberadamente, merece un comentario que diga por qué.fuente
^^
operador aún sería útil independientemente de la consideración de cortocircuito. A) evaluaría los operandos en un contexto booleano yb) garantizaría devolver un 1 o 0.inline bool XOR(bool a,bool b) { return a!=b; }
y obtener lo que desea, excepto que es una función en lugar de un operador (infijo). O podría usar!=
o sobrecargar directamente algún otro operador con este significado, pero luego, por supuesto, debe tener mucho cuidado de no terminar accidentalmente usando una sobrecarga no intencional con el mismo nombre. Y de paso||
y&&
volvertrue
ofalse
, no1
o0
.!
,||
y&&
evalúen sus argumentos en un contexto booleano es solo porque estos operadores rara vez están sobrecargados para aceptar otros tipos; Por lo que puedo decir, el lenguaje permite tales sobrecargas y, por lo tanto, no garantiza la evaluación de argumentos en un contexto booleano.Patrick hizo buenos puntos y no los voy a repetir. Sin embargo, podría sugerir reducir las declaraciones 'if' a un inglés legible siempre que sea posible utilizando variables booleanas bien nombradas. Por ejemplo, y esto está usando operadores booleanos, pero también podría usar bit a bit y nombrar los bools de manera apropiada:
bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here bool onlyBIsTrue = (b && !a); // and not need this second line if (onlyAIsTrue || onlyBIsTrue) { .. stuff .. }
Puede pensar que usar un booleano parece innecesario, pero ayuda con dos cosas principales:
EDITAR: No dijiste explícitamente que querías los condicionales para las declaraciones 'if' (aunque esto parece lo más probable), esa era mi suposición. Pero mi sugerencia de un valor booleano intermedio sigue en pie.
fuente
El uso de operaciones bit a bit para bool ayuda a ahorrar lógica de predicción de rama innecesaria por parte del procesador, resultante de una instrucción 'cmp' introducida por operaciones lógicas.
Reemplazar la lógica con operaciones bit a bit (donde todos los operandos son bool) genera un código más eficiente que ofrece el mismo resultado. Idealmente, la eficiencia debería superar todos los beneficios de cortocircuito que se pueden aprovechar en el pedido mediante operaciones lógicas.
Esto puede hacer que el código sea un poco ilegible, aunque el programador debería comentarlo con las razones por las que se hizo así.
fuente
IIRC, muchos compiladores de C ++ advertirán cuando intenten convertir el resultado de una operación bit a bit como bool. Tendría que usar un tipo de conversión para hacer feliz al compilador.
El uso de una operación bit a bit en una expresión if tendría la misma crítica, aunque quizás no por parte del compilador. Cualquier valor distinto de cero se considera verdadero, por lo que algo como "si (7 & 3)" será verdadero. Este comportamiento puede ser aceptable en Perl, pero C / C ++ son lenguajes muy explícitos. Creo que la ceja de Spock es debida diligencia. :) Agregaría "== 0" o "! = 0" para dejar perfectamente claro cuál era su objetivo.
Pero de todos modos, parece una preferencia personal. Ejecutaría el código a través de lint o una herramienta similar y vería si también cree que es una estrategia imprudente. Personalmente, parece un error de codificación.
fuente