Dlaczego najbardziej ujemna wartość int powoduje błąd dotyczący niejednoznacznych przeciążeń funkcji?

91

Dowiaduję się o przeciążaniu funkcji w C ++ i natknąłem się na to:

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

Z tego, co zrozumiałem, każda wartość podana w intzakresie (w moim przypadku intto 4 bajty) zostanie wywołana, display(int)a każda wartość spoza tego zakresu będzie niejednoznaczna (ponieważ kompilator nie może zdecydować, którą funkcję wywołać). Obowiązuje dla całego zakresu intwartości z wyjątkiem jego wartości minimalnej, tj. -2147483648Gdy kompilacja kończy się niepowodzeniem z powodu błędu

wywołanie przeciążenia display(long int)jest niejednoznaczne

Ale biorąc tę ​​samą wartość do an inti wypisując wartość daje 2147483648. Dosłownie jestem zdezorientowany tym zachowaniem.

Dlaczego to zachowanie jest obserwowane tylko wtedy, gdy zostanie przekazana najbardziej ujemna liczba? (Zachowanie jest takie samo, jeśli a shortjest używane z -32768- w rzeczywistości w każdym przypadku, gdy liczba ujemna i liczba dodatnia mają tę samą reprezentację binarną)

Używany kompilator: g ++ (GCC) 4.8.5

nieskończona pętla
źródło
4
Minimalna wartość int to „zgłoszenie błędu kompilatora”. Jaki błąd? Powinieneś to uwzględnić w pytaniu
Justin
11
Rozumiem call of overloaded ‘display(long int)’ is ambiguous.
crashmstr
6
Bez związku, ale powinieneś zaktualizować kompilator. Istnieje już GCC 7.1.
HolyBlackCat
4
Oto moje przypuszczenie: typeof(-2147483648) != int. Dosłowne to 2147483648, które jest za duże na an int, więc to jest longi jest negowane
Justin
3
Co ciekawe, g ++ (przynajmniej 6,4 i 7,1) nie narzeka, że int j{-2147483648};jest to zawężająca konwersja. Prawie warto zadać sobie to pytanie. Prawdopodobnie jest to związane z dopuszczaniem (np.) long longWartości constexpr, takich jak 2147483647LLzawężanie podczas inicjalizacji.
Toby Speight

Odpowiedzi:

145

To bardzo subtelny błąd. To, co widzisz, jest konsekwencją braku ujemnych literałów całkowitych w C ++. Jeśli spojrzymy na [lex.icon], otrzymamy literał całkowity ,

całkowity-literał
        dziesiętny-literał całkowity-sufiks opt
        [...]

może być literą dziesiętną ,

decimal-literal:
        niezerowa cyfra
        decimal-literal ' opt cyfra

gdzie cyfra jest [0-9]i niezerowe-cyfrowy jest [1-9]i par sufiks może być jeden u, U, l, L, ll, i LL. Nigdzie tutaj nie jest to -część literału dziesiętnego.

W §2.13.2 mamy również:

Całkowitą dosłowny jest ciągiem cyfr, które nie ma okresu lub wykładnik część, z opcjonalnymi oddzielających apostrofami, które są ignorowane przy ustalaniu jego wartości. Literał liczby całkowitej może mieć przedrostek określający jego podstawę i przyrostek określający jego typ. Leksykalnie pierwsza cyfra ciągu cyfr jest najbardziej znacząca. Dziesiętny całkowitą dosłownym (dziesięć zasada) zaczyna się od cyfr innym niż 0 ° C i składa się z szeregu cyfr.

(podkreślenie moje)

Co oznacza, że -in -2147483648jest jednostką jednoargumentową operator -. Oznacza to, że -2147483648jest faktycznie traktowany jako -1 * (2147483648). Ponieważ 2147483648jest o jeden za dużo dla Ciebie int, jest promowany do a, long inta niejednoznaczność wynika z tego, że nie pasuje.

Jeśli chcesz uzyskać minimalną lub maksymalną wartość dla typu w sposób przenośny, możesz użyć:

std::numeric_limits<type>::min();  // or max()
NathanOliver
źródło
2
-2147483647 - 1działałby również bez ostrzeżenia jako negatywne wyrażenie dosłowne
Cœur
2
Albo INT_MINnajmniej szczegółowa opcja. Jednak mniej ogólne.
MSalters
@NathanOliver, czy możesz mi uprzejmie wyjaśnić tę sprawę display(2147483649);. Dlaczego w tym przypadku nie może wywołać funkcji int bez znaku? i dlaczego traktuje argument 2147483649jako long int zamiast unsigned int?
nieskończona pętla,
2
@infiniteloop Dziesiętne literały liczb całkowitych przechodzą od intdo long intdo long long int. Nigdy nie otrzymasz typu bez znaku dla literału dziesiętnego, chyba że użyjesz sufiksu u/ U.
NathanOliver
2
W tym przykładzie tak. Aby zadzwonić display(unsigned a), potrzebujesz albo display(1234u);lub display(static_cast<unsigned>(1234));albounsigned foo = 1234; display(foo);
NathanOliver
36

Wyrażenie w -2147483648rzeczywistości stosuje -operator do stałej 2147483648. Na Twojej platformie intnie można przechowywać 2147483648, musi być reprezentowany przez większy typ. Dlatego wyrażenie -2147483648nie jest wyprowadzona być signed intjednak większa podpisał typ, signed long int.

Ponieważ nie zapewniasz przeciążenia, longkompilator jest zmuszony wybrać między dwoma przeciążeniami, które są jednakowo prawidłowe. Twój kompilator powinien wygenerować błąd kompilatora dotyczący niejednoznacznych przeciążeń.

François Andrieux
źródło
4

Poszerzanie odpowiedzi innych


Aby wyjaśnić, dlaczego PO jest zdezorientowany, najpierw : rozważ signed intbinarną reprezentację 2147483647poniżej.

Największy sygnowany int




Następnie dodaj jeden do tego numeru : podając inny signed intz -2147483648(którego PO chce użyć) Najmniejszy sygnowany int



Wreszcie: widzimy, dlaczego OP jest zdezorientowany podczas -2147483648kompilacji do a long intzamiast a signed int, ponieważ wyraźnie mieści się w 32 bitach.

Ale, jak wspominają obecne odpowiedzi, operator jednoargumentowy ( -) jest stosowany po rozwiązaniu, 2147483648który jest a long inti NIE mieści się w 32 bitach.

bunkerdive
źródło