¿Por qué el valor int más negativo causa un error sobre sobrecargas de funciones ambiguas?

91

Estoy aprendiendo sobre la sobrecarga de funciones en C ++ y encontré esto:

void display(int a)
{
    cout << "int" << endl;
}

void display(unsigned a)
{
    cout << "unsigned" << endl;
}

int main()
{
    int i = -2147483648;
    cout << i << endl; //will display -2147483648
    display(-2147483648);
}

Por lo que entendí, cualquier valor dado en el intrango (en mi caso intes de 4 bytes) llamará display(int)y cualquier valor fuera de este rango será ambiguo (ya que el compilador no puede decidir qué función llamar). Es válido para el rango completo de intvalores excepto su valor mínimo, es decir, -2147483648cuando la compilación falla con el error

la llamada de sobrecargado display(long int)es ambigua

Pero tomar el mismo valor en an inte imprimir el valor da 2147483648. Estoy literalmente confundido con este comportamiento.

¿Por qué se observa este comportamiento solo cuando se pasa el número más negativo? (El comportamiento es el mismo si shortse usa a con -32768- de hecho, en cualquier caso donde el número negativo y el número positivo tengan la misma representación binaria)

Compilador utilizado: g ++ (GCC) 4.8.5

Bucle infinito
fuente
4
El valor mínimo de Int es "arrojar un error de compilador". ¿Qué error? Debería incluirlo en la pregunta
Justin
11
Ya veo call of overloaded ‘display(long int)’ is ambiguous.
crashmstr
6
No relacionado, pero debe actualizar el compilador. Ya existe GCC 7.1.
HolyBlackCat
4
Aquí está mi suposición: typeof(-2147483648) != int. El literal es 2147483648, que es demasiado grande para un int, por lo que es un long, y está siendo negado
Justin
3
Curiosamente, g ++ (6.4 y 7.1, al menos) no se quejan de que int j{-2147483648};es una conversión estrecha. Casi vale la pena una pregunta en sí misma, eso. Probablemente esté relacionado con permitir (p. Ej.) long longValores constexpr, por ejemplo, que se reduzcan 2147483647LLen la inicialización.
Toby Speight

Respuestas:

145

Este es un error muy sutil. Lo que está viendo es una consecuencia de que no hay literales enteros negativos en C ++. Si miramos [lex.icon] obtenemos que un entero-literal ,

entero-literal
        decimal-literal entero-sufijo opt
        [...]

puede ser un decimal-literal ,

literal-decimal:
        dígito distinto de cero
         decimales literal ' opt dígitos

donde dígitos es [0-9]y distinto de cero dígitos es [1-9]y el sufijo par puede ser uno de u, U, l,L , ll, o LL. En ninguna parte aquí se incluye -como parte del literal decimal.

En §2.13.2, también tenemos:

Un literal entero es una secuencia de dígitos que no tiene punto ni parte de exponente, con comillas simples de separación opcionales que se ignoran al determinar su valor. Un literal entero puede tener un prefijo que especifica su base y un sufijo que especifica su tipo. El primer dígito léxico de la secuencia de dígitos es el más significativo. Un decimal literal entero (base diez) comienza con un dígito distinto de 0 y consta de una secuencia de dígitos decimales.

(énfasis mío)

Lo que significa que el -in -2147483648es el unariooperator - . Eso significa -2147483648que en realidad se trata como -1 * (2147483648). Como 2147483648es demasiado para ti int, se promueve along int y la ambigüedad proviene de que no coincide.

Si desea obtener el valor mínimo o máximo para un tipo de manera portátil, puede usar:

std::numeric_limits<type>::min();  // or max()
NathanOliver
fuente
2
-2147483647 - 1también funcionaría sin previo aviso como una expresión literal negativa
Cœur
2
O INT_MINpara la opción menos detallada. Aunque menos genérico.
MSalters
@NathanOliver, ¿podría explicarme amablemente este caso display(2147483649);? ¿Por qué no puede llamar a la función int sin firmar en este caso? y ¿por qué trata el argumento 2147483649como int largo en lugar de int sin firmar?
bucle infinito
2
@infiniteloop Los literales enteros decimales van de inta long inta long long int. Nunca obtendrá un tipo sin signo para un literal decimal a menos que use el sufijo u/ U.
NathanOliver
2
En este ejemplo sí. Para llamar display(unsigned a)necesita display(1234u);o display(static_cast<unsigned>(1234));ounsigned foo = 1234; display(foo);
NathanOliver
36

La expresión en -2147483648realidad aplica el -operador a la constante 2147483648. En su plataforma, intno se puede almacenar 2147483648, debe estar representado por un tipo más grande. Por lo tanto, -2147483648no se deduce que la expresión sea signed intun tipo con signo más grande,signed long int .

Dado que no proporciona una sobrecarga, longel compilador se ve obligado a elegir entre dos sobrecargas que son igualmente válidas. Su compilador debería emitir un error de compilador sobre sobrecargas ambiguas.

François Andrieux
fuente
4

Ampliando las respuestas de los demás


Para aclarar por qué se confunde el OP, primero : considere la signed intrepresentación binaria de 2147483647, a continuación.

Mayor int firmado




A continuación, agregue uno a este número : dando otro signed intde -2147483648(que el OP desea usar) Int firmado más pequeño



Finalmente: podemos ver por qué el OP se confunde cuando se -2147483648compila en a en long intlugar de a signed int, ya que claramente cabe en 32 bits.

Pero, como mencionan las respuestas actuales, el operador unario ( -) se aplica después de resolver 2147483648cuál es a long inty NO cabe en 32 bits.

búnker
fuente