Pregunta

¿Hay alguna razón para no utilizar los operadores bit a bit &, | y ^ para valores "bool" en C++?

A veces me encuentro con situaciones en las que quiero que exactamente una de dos condiciones sea verdadera (XOR), así que simplemente incluyo el operador ^ en una expresión condicional.A veces también quiero que se evalúen todas las partes de una condición, ya sea que el resultado sea verdadero o no (en lugar de hacer un cortocircuito), así que uso & y |.A veces también necesito acumular valores booleanos, y &= y |= pueden ser bastante útiles.

Algunos me sorprendieron 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?¿Existen compiladores modernos que den malos resultados para esto?

¿Fue útil?

Solución

|| y && son operadores booleanos y se garantiza que los integrados devolverán true o false.Nada más.

|, & y ^ son operadores bit a bit.Cuando el dominio de números con el que opera es solo 1 y 0, entonces son exactamente iguales, pero en los casos en los 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, el bool Se garantiza que el tipo será solo un true o un false (que se convierten implícitamente a respectivamente 1 y 0), por lo que es menos preocupante desde esta postura, pero el hecho de que la gente no esté acostumbrada a ver este tipo de cosas en el código es un buen argumento para no hacerlo.Sólo decir b = b && x y terminar con esto.

Otros consejos

Dos razones principales.En resumen, considérelo detenidamente;podría haber una buena razón para ello, pero si la hay, sea MUY explícito en sus comentarios porque puede ser frágil y, como usted mismo dice, la gente generalmente no está acostumbrada a ver código como este.

Xor bit a bit! = Xor lógico (excepto 0 y 1)

En primer lugar, si está operando con valores distintos a false y true (o 0 y 1, como números enteros), el ^ 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

Segundo, |, &, y ^, como operadores bit a bit, no provocan cortocircuitos.Además, múltiples operadores bit a bit encadenados en una sola declaración (incluso con paréntesis explícitos) se pueden reordenar optimizando los compiladores, porque las 3 operaciones normalmente son conmutativas.Esto es importante si el orden de las operaciones importa.

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 usted no controle los métodos a() y b(), o alguien más puede venir y cambiarlos más tarde sin entender la dependencia, y causar un error desagradable (y a menudo solo de versión-compilación).

Creo

a != b

es lo que quieres

Las cejas levantadas deberían decirte lo suficiente como para dejar de hacerlo.No escribes el código para el compilador, primero lo escribes para tus 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 bits, no para bools.
¿Supongo que también comes manzanas con un tenedor?Funciona pero sorprende a la gente así que es mejor no hacerlo.

Desventajas de los operadores a nivel de bits.

Usted pregunta:

"¿Hay alguna razón para no utilizar los operadores bit a bit?" &, |, y ^ para valores "bool" en C++?"

Sí el operadores logicos, que son los operadores booleanos de alto nivel incorporados !, && y ||, ofrecen las siguientes ventajas:

  • Garantizado conversión de argumentos a bool, es decir.a 0 y 1 valor ordinal.

  • Garantizado evaluación de cortocircuito donde la evaluación de la expresión se detiene tan pronto como se conoce el resultado final.
    Esto puede interpretarse como una lógica de valores de árbol, con Verdadero, FALSO y Indeterminado.

  • Equivalentes textuales legibles not, and y or, incluso si yo no los uso.
    Como señala el lector Antimony en un comentario, también los operadores de nivel de bits tienen tokens alternativos, a saber bitand, bitor, xor y compl, pero en mi opinión estos son menos legibles que and, or y not.

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 & 20, mientras 1 && 2true.También ^, bit a bit exclusivo o, puede comportarse mal de esta manera.Considerados como valores booleanos, 1 y 2 son iguales, es decir true, pero considerados como patrones de bits, son diferentes.


Como expresar lógico Cualquiera o en C++.

Luego proporciona algunos antecedentes para la pregunta,

"A veces me encuentro con situaciones en las que quiero que exactamente una de dos condiciones sea verdadera (XOR), así que simplemente incluyo el operador ^ en una expresión condicional".

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

a && b ^ c

obtienes el resultado quizás inesperado a && (b ^ c).

En lugar de eso, escribe solo

(a && b) != c

expresando de forma más concisa lo que quieres decir.

Para el argumento múltiple Cualquiera o no existe ningún operador de C++ que haga el trabajo.Por ejemplo, si escribes a ^ b ^ c que eso no es una expresión que diga “ya sea a, b o c es verdad".En lugar de eso dice: "Un número impar de a, b y c son verdaderas”, que podría ser 1 de ellas o las 3…

Para expresar el general uno/o cuando a, b y c son de tipo bool, solo escribe

(a + b + c) == 1

o, con no-bool argumentos, convertirlos a bool:

(!!a + !!b + !!c) == 1


Usando &= para acumular resultados booleanos.

Desarrollas más,

“A veces también necesito acumular valores booleanos, y &= y |=? puede ser muy útil”.

Bueno, esto corresponde a comprobar si respectivamente todo o cualquier se cumple la condición y ley de Morgan te dice cómo pasar de uno a otro.Es decir.solo necesitas uno de ellos.En principio podrías usar *= como un &&=-operador (porque, como descubrió el bueno de George Boole, el AND lógico se puede expresar muy fácilmente como multiplicación), pero creo que eso dejaría perplejos y tal vez 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:

true
false

La razón de la diferencia en los resultados es que el nivel de bits &= no proporciona una conversión a bool de su argumento del lado derecho.

Entonces, ¿cuál de estos resultados desea para el uso de &=?

Si lo anterior, true, entonces es mejor definir un operador (p. ej.como arriba) o función con nombre, o usar una conversión explícita de la expresión del lado derecho, o escribir la actualización completa.

Contrariamente a la respuesta de Patrick, C++ no tiene ^^ operador para realizar un cortocircuito exclusivo o.Si lo piensas por un segundo, tener una ^^ 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 la nobool Los tipos "booleanos" se mantienen igualmente bien cuando se comparan 1 & 2 a 1 && 2.Un ejemplo clásico de esto es Windows. GetMessage() función, que devuelve un triple estado BOOL:distinto de cero, 0, o -1.

Usando & en lugar de && y | en lugar de || No es un error tipográfico infrecuente, por lo que si lo haces deliberadamente, merece un comentario que diga por qué.

Patrick hizo buenos comentarios y no los voy a repetir.Sin embargo, podría sugerir reducir las declaraciones 'if' a un inglés legible siempre que sea posible mediante el uso de variables booleanas bien nombradas. Por ejemplo, y esto utiliza operadores booleanos, pero también podría usar bit a bit y nombrar los booleanos apropiadamente:

bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here
bool onlyBIsTrue = (b && !a); // and not need this second line
if (onlyAIsTrue || onlyBIsTrue)
{
 .. stuff ..
}

Podría pensar que usar un valor booleano parece innecesario, pero ayuda con dos cosas principales:

  • Su código es más fácil de entender porque el valor booleano intermedio para la condición 'si' hace que la intención de la condición sea más explícita.
  • Si está utilizando código no estándar o inesperado, como operadores bit a bit en valores booleanos, las personas podrán ver mucho más fácilmente por qué lo ha hecho.

EDITAR:No dijiste explícitamente que querías los condicionales para las declaraciones 'si' (aunque esto parece lo más probable), esa fue mi suposición.Pero mi sugerencia de un valor booleano intermedio sigue en pie.

IIRC, muchos compiladores de C++ advertirán cuando intenten convertir el resultado de una operación bit a bit como bool.Tendrías que usar una conversión de tipos para hacer feliz al compilador.

Usar una operación bit a bit en una expresión if generarí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 y 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.

El uso de operaciones bit a bit para bool ayuda a ahorrar lógica de predicción de bifurcación innecesaria por parte del procesador, como resultado de una instrucción 'cmp' traída por operaciones lógicas.

Reemplazar las operaciones lógicas con operaciones bit a bit (donde todos los operandos son booleanos) 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 al realizar pedidos mediante operaciones lógicas.

Esto puede hacer que el código sea un poco ilegible, aunque el programador debe comentarlo con las razones por las que lo hizo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top