¿Por qué MSVC no emite una advertencia para la comparación == firmado/sin firmar?
Estoy tratando de entender por qué el siguiente código no emite una advertencia en el lugar indicado.
//from limits.h
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define INT_MAX 2147483647 /* maximum (signed) int value */
/* = 0x7fffffff */
int a = INT_MAX;
//_int64 a = INT_MAX; // makes all warnings go away
unsigned int b = UINT_MAX;
bool c = false;
if(a < b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a > b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a <= b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a >= b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a == b) // no warning <--- warning expected here
c = true;
if(((unsigned int)a) == b) // no warning (as expected)
c = true;
if(a == ((int)b)) // no warning (as expected)
c = true;
Pensé que tenía que ver con la promoción de antecedentes, pero los dos últimos parecen decir lo contrario.
En mi opinión, ¿la primera ==
comparación es tan discordante entre firmado y sin firmar como las demás?
Al comparar firmado con sin firmar, el compilador convierte el valor firmado en sin firmar. Para la igualdad, esto no importa -1 == (unsigned) -1
. Para otras comparaciones es importante, por ejemplo, lo siguiente es cierto: -1 > 2U
.
EDITAR: Referencias:
5/9: (Expresiones)
Muchos operadores binarios que esperan operandos de tipo aritmético o de enumeración provocan conversiones y producen tipos de resultados de manera similar. El propósito es generar un tipo común, que también es el tipo del resultado. Este patrón se denomina conversiones aritméticas habituales, que se definen de la siguiente manera:
- Si cualquiera de los operandos es de tipo doble largo, el otro se convertirá en doble largo.
- De lo contrario, si alguno de los operandos es doble, el otro se convertirá en doble.
- De lo contrario, si alguno de los operandos es flotante, el otro se convertirá en flotante.
- En caso contrario, las promociones integrales (4.5) se realizarán en ambos operandos.54)
- Luego, si cualquiera de los operandos es largo sin signo, el otro se convertirá a largo sin signo.
- De lo contrario, si un operando es un int largo y el otro int sin signo, entonces si un int largo puede representar todos los valores de un int sin signo, el int sin signo se convertirá en un int largo; de lo contrario, ambos operandos se convertirán a un int largo sin signo.
- De lo contrario, si alguno de los operandos es largo, el otro se convertirá en largo.
- De lo contrario, si alguno de los operandos no tiene signo, el otro se convertirá en sin signo.
4.7/2: (Conversiones integrales)
Si el tipo de destino no tiene signo, el valor resultante es el número entero menos sin signo congruente con el entero de origen (módulo 2 n donde n es el número de bits utilizados para representar el tipo sin signo). [Nota: en una representación en complemento a dos, esta conversión es conceptual y no hay ningún cambio en el patrón de bits (si no hay truncamiento). ]
EDITAR2: niveles de advertencia de MSVC
Lo que se advierte en los diferentes niveles de advertencia de MSVC son, por supuesto, las decisiones tomadas por los desarrolladores. A mi modo de ver, sus elecciones en relación con la igualdad con/sin signo versus comparaciones mayores/menos tienen sentido, esto es completamente subjetivo, por supuesto:
-1 == -1
significa lo mismo que -1 == (unsigned) -1
: me parece un resultado intuitivo.
-1 < 2
no significa lo mismo que -1 < (unsigned) 2
: esto es menos intuitivo a primera vista y, en mi opinión, merece una advertencia "anterior".
El siguiente ejemplo demuestra por qué las advertencias firmadas/no firmadas son importantes y los programadores deben prestarles atención.
¿Adivina el resultado de este código?
#include <iostream>
int main() {
int i = -1;
unsigned int j = 1;
if ( i < j )
std::cout << " i is less than j";
else
std::cout << " i is greater than j";
return 0;
}
Producción:
i is greater than j
¿Sorprendido? Demostración en línea: http://www.ideone.com/5iCxY
En pocas palabras: en comparación, si un operando es unsigned
, entonces el otro operando se convierte implícitamente unsigned
si su tipo está firmado.