variable values utilizar type dato como booleano bool c++ boolean

values - Double Negation en C++ code



if bool c++ (14)

Acabo de entrar en un proyecto con una base de código bastante grande.

En su mayoría trato con C ++ y gran parte del código que escriben usa doble negación para su lógica booleana.

if (!!variable && (!!api.lookup("some-string"))) { do_some_stuff(); }

Sé que estos tipos son programadores inteligentes, es obvio que no están haciendo esto por accidente.

No soy un experto en C ++ experimentado, mi única conjetura de por qué lo están haciendo es que quieren hacer absolutamente positivo que el valor que se evalúa es la representación booleana real. Entonces lo niegan, luego lo niegan nuevamente para volver a su valor booleano real.

¿Es esto correcto, o me estoy perdiendo algo?


Como mencionó Marcin , bien podría importar si la sobrecarga del operador está en juego. De lo contrario, en C / C ++ no importa, excepto si estás haciendo una de las siguientes cosas:

  • comparación directa con true (o en C algo así como una macro TRUE ), que casi siempre es una mala idea. Por ejemplo:

    if (api.lookup("some-string") == true) {...}

  • simplemente quiere algo convertido a un valor estricto 0/1. En C ++ una asignación a bool hará esto implícitamente (para aquellas cosas que son implícitamente convertibles a bool ). En C o si se trata de una variable no bool, esta es una expresión que he visto, pero prefiero la (some_variable != 0) .

Creo que en el contexto de una expresión booleana más grande simplemente complica las cosas.


Da un paso lateral a una advertencia del compilador. Prueba esto:

int _tmain(int argc, _TCHAR* argv[]) { int foo = 5; bool bar = foo; bool baz = !!foo; return 0; }

La línea ''bar'' genera un "valor de forzado a bool ''verdadero'' o ''falso'' (advertencia de rendimiento)" en MSVC ++, pero la línea ''baz'' se escabulle muy bien.


En realidad, es una expresión muy útil en algunos contextos. Tome estas macros (ejemplo del kernel de Linux). Para GCC, se implementan de la siguiente manera:

#define likely(cond) (__builtin_expect(!!(cond), 1)) #define unlikely(cond) (__builtin_expect(!!(cond), 0))

¿Por qué tienen que hacer esto? El __builtin_expect de GCC trata sus parámetros como long y no bool , por lo que debe haber alguna forma de conversión. Como no saben qué condiciones tienen cuando escriben esas macros, es más general simplemente usarlas. idioma.

Probablemente podrían hacer lo mismo al comparar contra 0, pero en mi opinión, en realidad es más sencillo hacer la doble negación, ya que eso es lo más cercano a un elenco a bool que C tiene.

Este código también se puede usar en C ++ ... es una cosa del mínimo común denominador. Si es posible, haz lo que funciona tanto en C como en C ++.


Es correcto pero, en C, aquí no tiene sentido - ''if'' y ''&&'' tratarían la expresión de la misma manera sin el ''!!''.

La razón para hacer esto en C ++, supongo, es que ''&&'' podría estar sobrecargado. Pero entonces, también podría ''!'', Por lo que realmente no garantiza que obtenga un bool, sin mirar el código de los tipos de variable y api.call . Tal vez alguien con más experiencia en C ++ podría explicar; quizás sea una medida de defensa en profundidad, no una garantía.


Es el operador! ¿sobrecargado?
Si no, probablemente estén haciendo esto para convertir la variable en un bool sin producir una advertencia. Esta definitivamente no es una forma estándar de hacer las cosas.


Es un truco para convertir a bool.


Es una técnica para evitar escribir (variable! = 0), es decir, convertir de cualquier tipo que sea a un bool.

El código IMO como este no tiene cabida en los sistemas que deben mantenerse, porque no es un código inmediatamente legible (de ahí la pregunta, en primer lugar).

El código debe ser legible; de ​​lo contrario, deje un legado de deuda de tiempo para el futuro, ya que lleva tiempo entender algo que es innecesariamente intrincado.


Este puede ser un ejemplo del truco de doble explosión , ver The Safe Bool Idiom para más detalles. Aquí resumo la primera página del artículo.

En C ++ hay varias maneras de proporcionar pruebas booleanas para las clases.

Una forma obvia es el operator bool operador de conversión operator bool .

// operator bool version class Testable { bool ok_; public: explicit Testable(bool b=true):ok_(b) {} operator bool() const { // use bool conversion operator return ok_; } };

Podemos probar la clase,

Testable test; if (test) std::cout << "Yes, test is working!/n"; else std::cout << "No, test is not working!/n";

Sin embargo, opereator bool se considera inseguro porque permite operaciones sin sentido como la test << 1; o int i=test .

Usando el operator! es más seguro porque evitamos problemas de conversión implícita o sobrecarga.

La implementación es trivial,

bool operator!() const { // use operator! return !ok_; }

Las dos formas idiomáticas para probar el objeto Testable son

Testable test; if (!!test) std::cout << "Yes, test is working!/n"; if (!test2) { std::cout << "No, test2 is not working!/n";

La primera versión de if (!!test) es lo que algunas personas llaman el truco de doble explosión .


Los codificadores piensan que convertirá el operando a bool, pero como los operandos de && ya están convertidos implícitamente en bool, es completamente redundante.


Los desarrolladores de Legacy C no tenían un tipo Boolean, por lo que a menudo #define TRUE 1 y #define FALSE 0 y luego usaron tipos de datos numéricos arbitrarios para las comparaciones booleanas. Ahora que tenemos bool , muchos compiladores emitirán advertencias cuando se realicen ciertos tipos de asignaciones y comparaciones usando una combinación de tipos numéricos y booleanos. Estos dos usos eventualmente colisionarán cuando se trabaja con código heredado.

Para evitar este problema, algunos desarrolladores utilizan la siguiente identidad booleana !num_value devuelve bool true si num_value == 0 ; false contrario. !!num_value devuelve bool false si num_value == 0 ; true contrario. La única negación es suficiente para convertir num_value a bool ; sin embargo, la doble negación es necesaria para restaurar el sentido original de la expresión booleana.

Este patrón se conoce como una expresión idiomática , es decir, algo comúnmente utilizado por personas que están familiarizadas con el idioma. Por lo tanto, no lo veo como un antipatrón, tanto como lo haría static_cast<bool>(num_value) . El elenco podría dar los resultados correctos, pero algunos compiladores emiten una advertencia de rendimiento, por lo que todavía tiene que abordar eso.

La otra forma de abordar esto es decir, (num_value != FALSE) . También estoy de acuerdo con eso, ¡en general !!num_value es mucho menos detallado, puede ser más claro y no confunde la segunda vez que lo ves.


Sí, es correcto y no te falta algo. !! es una conversión a bool. Vea esta pregunta para más discusión.


Si la variable es de tipo de objeto, podría tener un! operador definido pero sin conversión a bool (o peor un lanzamiento implícito a int con semántica diferente. Llamar al operador! dos veces da como resultado una conversión a bool que funciona incluso en casos extraños.


Tal vez los programadores pensaban algo como esto ...

!! myAnswer es booleano. En contexto, debería volverse booleano, pero me encanta golpear las cosas para asegurarme, porque alguna vez hubo un error misterioso que me mordió, y bang bang, lo maté.


!! se usó para hacer frente a C ++ original que no tenía un tipo booleano (como tampoco lo hizo C).

Problema de ejemplo:

Dentro de if(condition) , la condition necesita evaluar a algún tipo como double, int, void* , etc., pero no bool ya que aún no existe.

Supongamos que existe una clase int256 (un entero de 256 bits) y todas las conversiones / moldes enteros están sobrecargadas.

int256 x = foo(); if (x) ...

Para probar si x era "verdadero" o distinto de cero, if (x) convertiría x en un entero y luego evaluaría si ese int fuera distinto de cero. Una sobrecarga típica de (int) x devolvería solo los LSbits de x . if (x) estaba solo probando los LSbits de x .

Pero C ++ tiene el ! operador. Una !x sobrecargada típicamente evaluaría todos los bits de x . Entonces, regrese a la lógica no invertida if (!!x) se usa.

Ref. ¿Las versiones anteriores de C ++ usaban el operador `int` de una clase cuando evaluaban la condición en una instrucción` if () `?