Acabo de llegar a un proyecto con una base de código bastante grande.
Principalmente estoy tratando 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 experimentado en C ++, mi única suposición de por qué están haciendo esto 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 correcto o me falta algo?

Respuestas:
Es un truco convertir a bool.
fuente
booltipo, para ayudar a evitar almacenar valores distintos de1y0en variables booleanas.!!o!=0resuelve el problema, y entre los dos encuentro el ex limpiador (ya que funcionará en una mayor cantidad de tipos). También estoy de acuerdo en que no hay ninguna razón para usar tampoco en el código en cuestión.En realidad, es un idioma muy útil en algunos contextos. Tome estas macros (ejemplo del kernel de Linux). Para GCC, se implementan de la siguiente manera:
¿Por qué tienen que hacer esto? GCC
__builtin_expecttrata sus parámetros comolongy nobool, por lo que debe haber alguna forma de conversión. Como no saben quécondes cuando escriben esas macros, lo más general es simplemente usar el!!idioma.Probablemente podrían hacer lo mismo comparando con 0, pero en mi opinión, en realidad es más sencillo hacer la doble negación, ya que es lo más cercano a un cast-to-bool que tiene C.
Este código también se puede usar en C ++ ... es una cosa de mínimo común denominador. Si es posible, haga lo que funciona tanto en C como en C ++.
fuente
Los codificadores piensan que convertirá el operando a bool, pero debido a que los operandos de && ya están implícitamente convertidos a bool, es completamente redundante.
fuente
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 legible de inmediato (de ahí la pregunta en primer lugar).
El código debe ser legible; de lo contrario, dejará un legado de deuda temporal para el futuro, ya que lleva tiempo comprender algo que es innecesariamente complicado.
fuente
bool(expr): hace lo correcto y todos entienden la intención a primera vista.!!(expr)es una doble negación, que accidentalmente se convierte en bool ... esto no es simple.Sí, es correcto y no, no te estás perdiendo algo.
!!Es una conversión a bool. Vea esta pregunta para más discusión.fuente
Evita una advertencia del compilador. Prueba esto:
La línea 'bar' genera un "valor de forzamiento para bool 'verdadero' o 'falso' (advertencia de rendimiento)" en MSVC ++, pero la línea 'baz' se cuela bien.
fuente
booltipo: todo está codificado como0o1en unint.Es operador! ¿sobrecargado?
Si no, probablemente estén haciendo esto para convertir la variable en un bool sin generar una advertencia. Definitivamente, esta no es una forma estándar de hacer las cosas.
fuente
Desarrolladores legado C no tenían ningún tipo booleano, lo que a menudo
#define TRUE 1y#define FALSE 0a continuación, utilizan tipos de datos numéricos arbitrarios para las comparaciones booleanas. Ahora que lo tenemosbool, muchos compiladores emitirán advertencias cuando se realicen ciertos tipos de asignaciones y comparaciones utilizando una mezcla de tipos numéricos y tipos booleanos. Estos dos usos eventualmente colisionarán cuando se trabaje con código heredado.Para solucionar este problema, algunos desarrolladores usan la siguiente identidad booleana:
!num_valuedevuelvebool trueifnum_value == 0;falsede otra manera.!!num_valuedevuelvebool falsesinum_value == 0;truede otra manera. La única negación es suficiente para convertirnum_valueabool; sin embargo, la doble negación es necesaria para restaurar el sentido original de la expresión booleana.Este patrón se conoce como modismo , es decir, algo comúnmente utilizado por personas 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 muy bien dar los resultados correctos, pero algunos compiladores luego emiten una advertencia de rendimiento, por lo que aún debe abordar eso.La otra manera de abordar esto es decir,
(num_value != FALSE). También estoy de acuerdo con eso, pero en general,!!num_valuees mucho menos detallado, puede ser más claro y no es confuso la segunda vez que lo ve.fuente
!!se utilizó para hacer frente a C ++ original que no tenía un tipo booleano (como tampoco lo hizo C).Problema de ejemplo:
En
if(condition)el interior , laconditionnecesidad de evaluar a algún tipo comodouble, int, void*, etc., pero noboolcomo todavía no existe.Digamos que existía una clase
int256(un entero de 256 bits) y todas las conversiones / conversiones de enteros estaban sobrecargadas.Para probar si
xera "verdadero" o distinto de cero,if (x)convertiríaxa algún número entero y luego evaluaría siintera distinto de cero. Una sobrecarga típica de(int) xdevolvería solo los LSbits dex.if (x)entonces solo estaba probando los LSbits dex.Pero C ++ tiene el
!operador. Una sobrecarga!xtípicamente evaluaría todos los bits dex. Entonces, para volver a la lógica no invertidaif (!!x)se utiliza.Ref. ¿Las versiones anteriores de C ++ utilizaron el operador `int` de una clase al evaluar la condición en una instrucción` if () `?
fuente
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á haciendo una de las siguientes cosas:
comparación directa con
true(o en C algo así como unaTRUEmacro), que casi siempre es una mala idea. Por ejemplo:if (api.lookup("some-string") == true) {...}simplemente desea que algo se convierta en un valor estricto de 0/1. En C ++, una asignación a una
boolvoluntad hará esto implícitamente (para aquellas cosas que son implícitamente convertibles abool). En C o si se trata de una variable no bool, este es un idioma que he visto, pero prefiero la(some_variable != 0)variedad yo mismo.Creo que en el contexto de una expresión booleana más grande, simplemente llena las cosas.
fuente
Si la variable es de tipo de objeto, puede tener un! operador definido pero sin conversión a bool (o peor, una conversión implícita a int con semántica diferente. Llamar al operador! dos veces resulta en una conversión a bool que funciona incluso en casos extraños.
fuente
Es correcto pero, en C, no tiene sentido aquí: '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 no garantiza realmente que obtenga un bool, sin mirar el código para los tipos de
variableyapi.call. Quizás alguien con más experiencia en C ++ podría explicarlo; tal vez sea una medida de defensa en profundidad, no una garantía.fuente
ifo&&, pero el uso!!puede ayudar en algunos compiladores siif (!!(number & mask))se reemplaza conbit triggered = !!(number & mask); if (triggered); en algunos compiladores integrados con tipos de bits, asignar, por ejemplo, 256 a un tipo de bits producirá cero. Sin la!!transformación aparentemente segura (copiar laifcondición a una variable y luego ramificar) no será segura.Tal vez los programadores estaban pensando algo como esto ...
!! myAnswer es booleano. En contexto, debería volverse booleano, pero me encanta golpear las cosas para asegurarme, porque había una vez un misterioso error que me mordió, y lo golpeé, lo maté.
fuente
Este puede ser un ejemplo del truco de la doble explosión , vea The Safe Bool Idiom para más detalles. Aquí resumo la primera página del artículo.
En C ++ hay varias formas de proporcionar pruebas booleanas para las clases.
Podemos probar la clase,
Sin embargo,
opereator boolse considera inseguro porque permite operaciones sin sentido comotest << 1;oint i=test.La implementación es trivial,
Las dos formas idiomáticas de probar
Testableobjetos sonLa primera versión
if (!!test)es lo que algunas personas llaman el truco del doble golpe .fuente
explicit operator boolpara evitar la conversión implícita a otros tipos integrales.