2011-05-09 13 views
15

Sin pensarlo me escribió algo de código para comprobar que todos los valores de una estructura se ponen a 0. Para lograr esto he utilizado:valores de encadenamiento de Bool dan resultado contrario al esperado

bool IsValid() { 
    return !(0 == year == month == day == hour == minute == second); 
} 

donde todos los miembros de la estructura eran de tipo sin signo corto. Usé el código como parte de una prueba más grande, pero me di cuenta de que volvía falso para valores que diferían de cero y verdadero para valores que eran todos iguales a cero, lo contrario de lo que esperaba.

he cambiado el código para leer:

bool IsValid() { 
    return (0 != year) || (0 != month) || (0 != day) || (0 != hour) || (0 != minute) || (0 != second); 
} 

pero me gustaría saber qué causó el comportamiento extraño. ¿Es un resultado de precedencia? Intenté buscar esta respuesta en Google, pero no encontré nada, si hay alguna nomenclatura para describir el resultado, me encantaría saberlo.

Compilé el código usando VS9 y VS8.

+0

Sin darse cuenta tropezó con las reglas de promoción integral. Un bool puede ser promovido a un entero, por lo que la prueba 'bool == int' es válida ... incluso si es extremadamente contrario a la intuición. Ah! –

+0

Como nota, 'a == b == c == d == 0' funcionaría en Python. – liori

Respuesta

26

== grupos de izquierda a derecha, por lo que si todos los valores son cero, entonces:

0 == year // true 
(0 == year) == month // false, since month is 0 and (0 == year) converts to 1 
((0 == year) == month) == day // true 

Y así sucesivamente.

En general, x == y == z es no equivalente a x == y && x == z como parece esperar.

+0

Esta es mi respuesta favorita con respecto a la simplicidad. Charles Bailey lo cubrió con precisión primero, pero tu respuesta aporta una claridad muy apreciada. Es extraño pensar que asumí que el compilador * sabría * lo que quise decir. – 17Twenty

15

El comportamiento no debe verse como impar. Las reglas gramaticales para == (y la mayoría pero no todos los operadores binarios) especificar izquierda a derecha agrupación por lo que su expresión original es equivalente a:

!((((((0 == year) == month) == day) == hour) == minute) == second) 

Tenga en cuenta que cuando se compara con un tipo entero una expresión bool con valor true promoverá a 1 y con el valor false se promocionará al 0. (En C el resultado del operador de igualdad es un int en todo caso con un valor o bien 1 o 0.)

Esto significa que, por ejemplo, ((0 == year) == month) será verdadera si year es cero y month es uno o si year es distinto de cero pero month es cero y de lo contrario es falso.

8

Usted tiene que considerar cómo se evalúa ...

a == b == c 

está preguntando si dos de ellos son iguales (a y b), luego comparar ese resultado booleano para el tercer valor c! NO está comparando los primeros dos valores con el tercero. Cualquier cosa más allá de 2 argumentos no encadena como es de esperar.

Por lo que vale la pena, porque C++ considera que no son valores 0 a ser "verdadero" en un contexto booleano, puede expresar lo que quiere simplemente como:

return year && month && day && hour && minute && second; 

(nota: su código revisado dice " mes "dos veces y no prueba el minuto").

Volver a los encadenados == s: con tipos definidos por el usuario y el operador que sobrecarga puede crear una clase que compara la forma esperada (e incluso puede permitir cosas como 0 <= x < 10 a "trabajar" en la forma en que se lee en Matemáticas), pero crear algo especial solo confundirá a otros programadores que ya conocen la (extraña) forma en que estas cosas funcionan para los tipos integrados en C++. Vale la pena hacer un ejercicio de programación de diez/veinte minutos, aunque si está interesado en aprender C++ en profundidad (sugerencia: necesita que los operadores de comparación devuelvan un objeto proxy que recuerda cuál será el valor del lado izquierdo para la siguiente comparación operador).

Finalmente, a veces estas expresiones booleanas "raras" son útiles: por ejemplo, a == b == (c == d) podría estar redactado en inglés como "cualquiera (a == b) y (c == d), OR (a! = B) y (c! = d) ", o quizás" la equivalencia de a y b es la misma que la equivalencia de cyd (no importa si es verdadero o falso) ". Eso podría modelar situaciones del mundo real como un escenario de citas dobles: si a alguien le gusta/no le gusta b (su fecha) tanto como c le gusta/no le gusta d, entonces se quedará o pasará un buen rato o lo abandonará rápidamente y es indoloro de cualquier manera ... de lo contrario, una pareja tendrá un momento muy tedioso ... Debido a que estas cosas pueden tener sentido, es imposible para el compilador saber que no tenía la intención de crear dicha expresión.

+0

Bien manchado - m falla de mi parte. He escrito C++ durante bastante tiempo y yo, y un programador senior, no estábamos de acuerdo con esto, aunque cuando se ha razonado como lo han hecho tú y otros, se vuelve obvio. Lanzamos pelusas a nuestro código y nos sorprendió que no lo haya entendido y que el compilador no * sepa * lo que quise decir, en última instancia, una falla de mi parte, y estoy feliz de aprender de ello. Gracias. – 17Twenty

+0

@ 17 Veinte: Sorprende a todos al principio: se parece a las matemáticas, pero se comporta de manera totalmente diferente. Me pregunto qué idiomas lo hacen de manera diferente (tal vez Miranda - han pasado ~ 20 años desde que usé eso). Agregué una nota al último párrafo sobre por qué lint/the compiler etc. no puede diagnosticar esto sin arriesgar falsos positivos. Aclamaciones. –

+0

Ejemplo interesante que podría ser un falso positivo, pero en C++ tu ejemplo compara dos 'bool', mientras que el código del interrogador compara un' bool' con un 'corto'. Por lo tanto, un compilador de C++ podría advertir a uno pero no al otro, aunque puede haber otros ejemplos que lo estropeen. En C, ambos comparan tipos enteros "propios". –

1

El retorno del operador == es 1 si los operandos son iguales, por lo que independientemente de si se lee de izquierda a derecha o de derecha a izquierda, esto no hará lo que espera.

por lo que esto solo podría funcionar en una prueba análoga si le interesaría si todos los valores son 1.

y tener una expresión más corta ya que parece interesado en que acaba de hacer year || day || ...

1

Su error aquí está escribiendo una expresión matemática usando signos de igual, y sin pensarlo, suponiendo que el equipo llevará a cabo la prueba que quería decir - lo que un matemático humano vería como el significado de esos símbolos. Lo que hace la computadora (según la definición del lenguaje) es realizar una serie de comparaciones discretas, cada una de las cuales devuelve true o false - y esta true o falsese usa luego en la siguiente comparación. No está comparando todas esas variables a 0, está comparando cada una (barra dos de ellas) con el resultado de comparar otras dos de dichas variables.

Cuestiones relacionadas