Por que o valor int mais negativo causa um erro sobre sobrecargas de função ambíguas?

91

Estou aprendendo sobre sobrecarga de função em C ++ e me deparei com o seguinte:

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);
}

Pelo que entendi, qualquer valor dado no intintervalo (no meu caso inté 4 bytes) será chamado display(int)e qualquer valor fora desse intervalo será ambíguo (já que o compilador não pode decidir qual função chamar). É válido para toda a gama de intvalores excepto o seu valor mínimo, isto é, -2147483648onde a compilação falha com o erro

chamada de sobrecarregado display(long int)é ambígua

Mas pegar o mesmo valor para um inte imprimir o valor dá 2147483648. Estou literalmente confuso com esse comportamento.

Por que esse comportamento é observado apenas quando o número mais negativo é passado? (O comportamento é o mesmo se a shortfor usado com -32768- de fato, em qualquer caso onde o número negativo e o número positivo têm a mesma representação binária)

Compilador usado: g ++ (GCC) 4.8.5

Loop infinito
fonte
4
O valor mínimo de Int é "lançando um erro do compilador". Que erro? Você deve incluí-lo na pergunta
Justin
11
Eu vejo call of overloaded ‘display(long int)’ is ambiguous.
crashmstr de
6
Não relacionado, mas você deve atualizar o compilador. Já existe o GCC 7.1.
HolyBlackCat
4
Aqui é o meu palpite: typeof(-2147483648) != int. O literal é 2147483648, que é muito grande para um int, então é um long, e está sendo negado
Justin
3
Curiosamente, g ++ (6.4 e 7.1, pelo menos) não reclama que int j{-2147483648};é uma conversão de estreitamento. Quase vale a pena uma pergunta, isso. Provavelmente está relacionado a permitir (por exemplo) long longvalores constexpr como 2147483647LLestreitos na inicialização.
Toby Speight

Respostas:

145

Este é um erro muito sutil. O que você está vendo é uma consequência de não haver literais inteiros negativos em C ++. Se olharmos para [lex.icon], obtemos um literal inteiro ,

literal inteiro
         decimal-literal inteiro-sufixo opt
        [...]

pode ser um literal decimal ,

literal decimal:
        dígito diferente de zero
        decimal literal ' dígito opt

onde dígitos é [0-9]e diferente de zero dígitos é [1-9]e o sufixo nominal pode ser uma de u, U, l, L, ll, ou LL. Em nenhum lugar aqui ele inclui -como parte do literal decimal.

Em §2.13.2, também temos:

Um literal inteiro é uma sequência de dígitos que não tem ponto ou parte expoente, com aspas simples de separação opcionais que são ignoradas ao determinar seu valor. Um literal inteiro pode ter um prefixo que especifica sua base e um sufixo que especifica seu tipo. O primeiro dígito lexicamente da sequência de dígitos é o mais significativo. Um literal inteiro decimal (base dez) começa com um dígito diferente de 0 e consiste em uma sequência de dígitos decimais.

(ênfase minha)

O que significa que o -in -2147483648é unário operator -. Isso significa que -2147483648é realmente tratado como -1 * (2147483648). Como 2147483648é demais para você, intele é promovido long inta ae a ambigüidade vem dessa não correspondência.

Se você deseja obter o valor mínimo ou máximo para um tipo de maneira portátil, pode usar:

std::numeric_limits<type>::min();  // or max()
NathanOliver
fonte
2
-2147483647 - 1também funcionaria sem aviso como uma expressão literal negativa
Cœur
2
Ou INT_MINpara a opção menos detalhada. Menos genérico, porém.
MSalters de
@NathanOliver, você pode me explicar esse caso display(2147483649);. Por que ele não pode chamar a função int sem sinal neste caso? e por que ele trata o arg 2147483649como long int em vez de unsigned int?
loop infinito de
2
@infiniteloop Literais inteiros decimais vão de inta long intpara long long int. Você nunca obterá um tipo sem sinal para um literal decimal, a menos que use o sufixo u/ U.
NathanOliver
2
Neste exemplo, sim. Para ligar, display(unsigned a)você precisa de display(1234u);ou display(static_cast<unsigned>(1234));ouunsigned foo = 1234; display(foo);
NathanOliver
36

A expressão -2147483648está, na verdade, aplicando o -operador à constante 2147483648. Na sua plataforma, intnão pode armazenar 2147483648, deve ser representado por um tipo maior. Portanto, a expressão -2147483648não é deduzida como sendo signed intum tipo maior com sinal signed long int,.

Como você não fornece uma sobrecarga para longo compilador, ele é forçado a escolher entre duas sobrecargas que são igualmente válidas. Seu compilador deve emitir um erro do compilador sobre sobrecargas ambíguas.

François Andrieux
fonte
4

Expandindo as respostas dos outros


Para esclarecer porque o OP é confuso, primeiro : considere a signed intrepresentação binária de 2147483647, abaixo.

Maior int assinado




Em seguida, adicione um a este número : dando outro signed intde -2147483648(que o OP deseja usar) Menor int assinado



Finalmente: podemos ver porque o OP fica confuso quando -2147483648compila para um em long intvez de umsigned int , já que ele claramente se encaixa em 32 bits.

Mas, como as respostas atuais mencionam, o operador unário ( -) é aplicado após a resolução 2147483648que é a long inte NÃO cabe em 32 bits.

bunkerdive
fonte