Mengapa nilai int paling negatif menyebabkan kesalahan tentang kelebihan beban fungsi yang ambigu?

91

Saya belajar tentang fungsi overloading di C ++ dan menemukan ini:

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

Dari apa yang saya pahami, nilai apa pun yang diberikan dalam intkisaran (dalam kasus saya intadalah 4 byte) akan memanggil display(int)dan nilai apa pun di luar kisaran ini akan menjadi ambigu (karena penyusun tidak dapat memutuskan fungsi mana yang akan dipanggil). Ini valid untuk rentang intnilai lengkap kecuali nilai minnya, yaitu di -2147483648mana kompilasi gagal dengan kesalahan

panggilan overloaded display(long int)ambigu

Tapi mengambil nilai yang sama intdan mencetak nilai yang diberikan 2147483648. Saya benar-benar bingung dengan perilaku ini.

Mengapa perilaku ini diamati hanya ketika angka paling negatif dilewatkan? (Perilakunya sama jika a shortdigunakan dengan -32768- pada kenyataannya, dalam kasus apa pun di mana bilangan negatif dan bilangan positif memiliki representasi biner yang sama)

Kompiler yang digunakan: g ++ (GCC) 4.8.5

loop tak terbatas
sumber
4
Nilai min Int adalah "melempar kesalahan kompilator". Kesalahan apa? Anda harus memasukkannya ke dalam pertanyaan
Justin
11
Aku mengerti call of overloaded ‘display(long int)’ is ambiguous.
crashmstr
6
Tidak terkait, tetapi Anda harus memperbarui kompilernya. Sudah ada GCC 7.1.
HolyBlackCat
4
Berikut saya duga: typeof(-2147483648) != int. Literalnya adalah 2147483648, yang terlalu besar untuk sebuah int, jadi itu a long, dan itu sedang dinegasikan
Justin
3
Menariknya, g ++ (setidaknya 6.4 dan 7.1) tidak mengeluh bahwa int j{-2147483648};ini adalah konversi yang mempersempit. Hampir layak menjadi pertanyaan tersendiri, itu. Ini mungkin terkait dengan mengizinkan (misalnya) long longnilai-nilai konstekspr seperti yang 2147483647LLdipersempit dalam inisialisasi.
Toby Speight

Jawaban:

145

Ini adalah kesalahan yang sangat halus. Apa yang Anda lihat adalah konsekuensi dari tidak adanya literal integer negatif dalam C ++. Jika kita melihat [lex.icon] kita mendapatkan bahwa literal-integer ,

integer-literal
        desimal-literal integer-suffix opt
        [...]

bisa berupa desimal-literal ,

desimal-literal:
        nol-digit
        desimal-literal ' opt digit

di mana digit adalah [0-9]dan nol-digit adalah [1-9]dan akhiran par dapat menjadi salah satu dari u, U, l, L, ll, atau LL. Tidak ada di sini yang memasukkannya -sebagai bagian dari literal desimal.

Dalam §2.13.2, kami juga memiliki:

Sebuah literal bilangan bulat adalah urutan angka yang tidak memiliki periode atau bagian eksponen, dengan opsional memisahkan tanda kutip tunggal yang diabaikan saat menentukan nilainya. Literal integer mungkin memiliki awalan yang menentukan basisnya dan sufiks yang menentukan tipenya. Digit pertama secara leksikal dari urutan angka adalah yang paling signifikan. Sebuah desimal integer literal (dasar sepuluh) dimulai dengan angka selain 0 dan terdiri dari urutan angka desimal.

(penekanan saya)

Yang artinya -in -2147483648adalah unary operator -. Artinya -2147483648sebenarnya diperlakukan sebagai -1 * (2147483648). Karena 2147483648satu terlalu banyak untuk Anda, intitu dipromosikan menjadi a long intdan ambiguitas berasal dari ketidaksesuaian itu.

Jika Anda ingin mendapatkan nilai minimum atau maksimum untuk suatu jenis secara portabel, Anda dapat menggunakan:

std::numeric_limits<type>::min();  // or max()
NathanOliver
sumber
2
-2147483647 - 1juga akan bekerja tanpa peringatan sebagai ekspresi literal negatif
Cœur
2
Atau INT_MINuntuk opsi verbose terkecil. Kurang umum.
MSalters
@NathanOliver, Bisakah Anda menjelaskan kasus ini kepada saya display(2147483649);. Mengapa tidak dapat memanggil fungsi int unsigned dalam kasus ini? dan mengapa itu memperlakukan arg 2147483649as long int daripada unsigned int?
loop tak terbatas
2
@infiniteloop Decimal bilangan bulat literal pergi dari intke long intke long long int. Anda tidak akan pernah mendapatkan tipe unsigned untuk literal desimal kecuali Anda menggunakan sufiks u/ U.
NathanOliver
2
Dalam contoh ini ya. Untuk menelepon display(unsigned a)Anda perlu display(1234u);atau display(static_cast<unsigned>(1234));atauunsigned foo = 1234; display(foo);
NathanOliver
36

Ekspresi -2147483648sebenarnya menerapkan -operator ke konstanta 2147483648. Di platform Anda, inttidak bisa menyimpan 2147483648, itu harus diwakili oleh tipe yang lebih besar. Oleh karena itu, ekspresi -2147483648tidak disimpulkan menjadi signed inttetapi tipe bertanda tangan yang lebih besar signed long int,.

Karena Anda tidak memberikan kelebihan beban long, kompilator dipaksa untuk memilih di antara dua beban berlebih yang keduanya sama-sama valid. Kompilator Anda harus mengeluarkan kesalahan kompilator tentang kelebihan beban yang ambigu.

François Andrieux
sumber
4

Memperluas jawaban orang lain


Untuk memperjelas mengapa OP bingung, pertama : pertimbangkan signed intrepresentasi biner 2147483647, di bawah ini.

Int bertanda tangan terbesar




Berikutnya, tambahkan satu ke nomor ini : memberikan lain signed intdari -2147483648(yang OP ingin digunakan) Int bertanda tangan terkecil



Akhirnya: kita dapat melihat mengapa OP bingung ketika -2147483648mengkompilasi ke a, long intbukan a signed int, karena jelas cocok dalam 32 bit.

Tapi, seperti jawaban saat ini menyebutkan, operator unary ( -) diterapkan setelah menyelesaikan 2147483648yang merupakan long intdan TIDAK muat dalam 32 bit.

bunkerdive
sumber