Perché il valore int più negativo causa un errore sui sovraccarichi di funzioni ambigui?


91

Sto imparando a conoscere il sovraccarico di funzioni in C ++ e mi sono imbattuto in questo:

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

Da quello che ho capito, qualsiasi valore fornito intnell'intervallo (nel mio caso intè 4 byte) verrà chiamato display(int)e qualsiasi valore al di fuori di questo intervallo sarà ambiguo (poiché il compilatore non può decidere quale funzione chiamare). È valido per l'intera gamma di intvalori eccetto il suo valore minimo, cioè -2147483648dove la compilazione fallisce con l'errore

la chiamata di sovraccarico display(long int)è ambigua

Ma prendendo lo stesso valore ad an inte stampando il valore si ottiene 2147483648. Sono letteralmente confuso con questo comportamento.

Perché questo comportamento viene osservato solo quando viene passato il numero più negativo? (Il comportamento è lo stesso se shortsi usa a -32768- infatti, in ogni caso dove il numero negativo e il numero positivo hanno la stessa rappresentazione binaria)

Compilatore utilizzato: g ++ (GCC) 4.8.5


4
Il valore minimo di Int è "generare un errore del compilatore". Quale errore? Dovresti includerlo nella domanda
Justin

11
Capisco call of overloaded ‘display(long int)’ is ambiguous.
crashmstr

6
Non correlato, ma dovresti aggiornare il compilatore. Esiste già GCC 7.1.
HolyBlackCat

4
Ecco la mia ipotesi: typeof(-2147483648) != int. Il letterale è 2147483648, che è troppo grande per un int, quindi è un long, e viene negato
Justin

3
È interessante notare che g ++ (6.4 e 7.1, almeno) non si lamentano che int j{-2147483648};è una conversione restrittiva. Quasi merita una domanda in sé, questo. Probabilmente è correlato a consentire (ad esempio) i long longvalori di constexpr come 2147483647LLessere ristretti nell'inizializzazione .
Toby Speight

Risposte:


145

Questo è un errore molto sottile. Quello che vedi è una conseguenza dell'assenza di valori letterali interi negativi in ​​C ++. Se guardiamo [lex.icon] otteniamo che un intero-letterale ,

intero-letterale
        decimale-letterale intero-suffisso opt
        [...]

può essere un decimale-letterale ,

decimale-letterale:
        cifra
        decimale-letterale ' opt digit

dove cifre è [0-9]e diverso da zero cifre è [1-9]e par suffisso può essere uno dei u, U, l, L, ll, o LL. Da nessuna parte qui include -come parte del letterale decimale.

Nel §2.13.2 abbiamo anche:

Un valore letterale intero è una sequenza di cifre che non ha un punto o una parte esponente, con virgolette singole facoltative che vengono ignorate durante la determinazione del valore. Un valore letterale intero può avere un prefisso che ne specifica la base e un suffisso che ne specifica il tipo. La prima cifra lessicale della sequenza di cifre è la più significativa. Un valore letterale intero decimale (base dieci) inizia con una cifra diversa da 0 e consiste in una sequenza di cifre decimali.

(enfasi mia)

Il che significa che l' -in -2147483648è unario operator -. Ciò significa che -2147483648viene effettivamente trattato come -1 * (2147483648). Poiché 2147483648è uno di troppo per te int, viene promosso a long inte l'ambiguità deriva da quella non corrispondenza.

Se vuoi ottenere il valore minimo o massimo per un tipo in modo portatile puoi usare:

std::numeric_limits<type>::min();  // or max()

2
-2147483647 - 1funzionerebbe anche senza preavviso come espressione letterale negativa
Cœur

2
O INT_MINper l'opzione meno prolissa. Meno generico, però.
MSalters

@ NathanOliver, puoi spiegarmi gentilmente questo caso display(2147483649);. Perché in questo caso non può chiamare la funzione int senza segno? e perché tratta l'arg 2147483649come long int invece di unsigned int?
loop infinito

2
@infiniteloop I valori letterali interi decimali vanno da inta long inta long long int. Non otterrai mai un tipo senza segno per un letterale decimale a meno che non usi il suffisso u/ U.
NathanOliver

2
In questo esempio sì. Per chiamare display(unsigned a)è necessario display(1234u);o display(static_cast<unsigned>(1234));ounsigned foo = 1234; display(foo);
NathanOliver

36

L'espressione -2147483648sta effettivamente applicando l' -operatore alla costante 2147483648. Sulla tua piattaforma, intnon puoi memorizzare 2147483648, deve essere rappresentato da un tipo più grande. Pertanto, l'espressione -2147483648non è dedotto di essere signed int, ma una più grande tipo firmato, signed long int.

Poiché non si fornisce un overload per longil compilatore è costretto a scegliere tra due overload che siano entrambi ugualmente validi. Il compilatore dovrebbe emettere un errore del compilatore su sovraccarichi ambigui.


4

Espandendo le risposte degli altri


Per chiarire perché l'OP è confuso, prima : considera la signed intrappresentazione binaria di 2147483647, di seguito.

Int firmato più grande




Quindi, aggiungine uno a questo numero : dandone un altro signed intdi -2147483648(che l'OP desidera utilizzare) Int. Firmato più piccolo



Infine: possiamo vedere perché l'OP è confuso quando -2147483648compila in a long intinvece di a signed int, poiché si adatta chiaramente a 32 bit.

Ma, come menzionano le risposte correnti, l'operatore unario ( -) viene applicato dopo aver risolto 2147483648che è a long inte NON si adatta a 32 bit.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.