int numeral -> aturan konversi pointer

19

Pertimbangkan kode berikut.

void f(double p) {}
void f(double* p) {}

int main()
{ f(1-1); return 0; }

MSVC 2017 tidak mengkompilasi itu. Itu angka ada panggilan ambigu yang berlebihan, seperti 1-1yang sama 0dan karena itu dapat dikonversi menjadi double*. Trik lain, seperti 0x0, 0L, atau static_cast<int>(0), tidak bekerja baik. Bahkan mendeklarasikan const int Zero = 0dan menelepon f(Zero)menghasilkan kesalahan yang sama. Ini hanya berfungsi dengan baik jika Zerotidak const.

Sepertinya masalah yang sama berlaku untuk GCC 5 dan di bawah, tetapi bukan GCC 6. Saya ingin tahu apakah ini adalah bagian dari standar C ++, bug MSVC yang diketahui, atau pengaturan dalam kompiler. Google sepintas tidak membuahkan hasil.

pengguna1334767
sumber

Jawaban:

18

MSVC dianggap 1-1sebagai konstanta penunjuk nol. Ini benar oleh standar untuk C + + 03, di mana semua ekspresi konstanta integral dengan nilai 0adalah konstanta pointer nol, tetapi itu diubah sehingga hanya nol integer literal konstanta pointer nol untuk C ++ 11 dengan masalah CWG 903 . Ini adalah perubahan yang melanggar, seperti yang Anda lihat dalam contoh Anda dan sebagaimana juga didokumentasikan dalam standar, lihat [diff.cpp03.conv] dari standar C ++ 14 (draft N4140).

MSVC menerapkan perubahan ini hanya dalam mode kesesuaian. Jadi kode Anda akan dikompilasi dengan /permissive-bendera, tapi saya pikir perubahan hanya diterapkan di MSVC 2019, lihat di sini .

Dalam kasus GCC, GCC 5 default ke mode C ++ 98, sedangkan GCC 6 dan kemudian default ke mode C ++ 14, itulah sebabnya mengapa perubahan perilaku tampaknya tergantung pada versi GCC.

Jika Anda menelepon fdengan konstanta penunjuk nol sebagai argumen, maka panggilan tersebut ambigu, karena konstanta penunjuk nol dapat dikonversi ke nilai penunjuk nol dari semua jenis penunjuk dan konversi ini memiliki peringkat yang sama dengan konversi int(atau tipe integral apa pun) untuk double.

kenari
sumber
-1

Kompiler bekerja dengan benar, sesuai dengan [over.match] dan [conv] , lebih khusus [conv.fpint] dan [conv.ptr].

Urutan konversi standar adalah [bla bla] Nol atau satu [...] konversi integral-mengambang, konversi penunjuk, [...].

dan

Nilai awal tipe integer atau tipe enumerasi yang tidak dicopot dapat dikonversi ke nilai awal tipe floating-point. Hasilnya tepat jika memungkinkan [bla bla]

dan

Konstanta penunjuk nol adalah bilangan bulat integer dengan nilai nol atau [...]. Konstanta penunjuk nol dapat dikonversi ke jenis penunjuk; hasilnya adalah nilai pointer nol dari tipe itu [bla bla]

Sekarang, resolusi kelebihan adalah untuk memilih yang paling cocok di antara semua fungsi kandidat (yang, sebagai fitur menyenangkan, bahkan tidak perlu diakses di lokasi panggilan!). Kecocokan terbaik adalah yang dengan parameter tepat atau, sebagai alternatif, konversi sesedikit mungkin. Nol atau satu konversi standar dapat terjadi (... untuk setiap parameter), dan nol "lebih baik" dari satu.

(1-1)adalah integer literal dengan nilai 0.

Anda dapat mengonversi nol integer literal ke masing - masing salah satu doubleatau double*(atau nullptr_t), dengan tepat satu konversi. Jadi, dengan asumsi bahwa lebih dari satu fungsi ini dinyatakan (seperti halnya dalam contoh), ada lebih dari satu kandidat, dan semua kandidat sama-sama baik, tidak ada yang paling cocok. Ini ambigu, dan kompilernya benar untuk mengeluh.

Damon
sumber
1
Bagaimana 1-1sebuah literal bilangan bulat ? Ini adalah ekspresi yang mengandung dua literal bilangan bulat dengan nilai 1dan -operator.
walnut
@walnut: Anda mungkin merujuk pada kata-kata "urutan biner-digit, oktal-digit, digit, atau hexadecimal-digit" . Itu kata yang sangat disayangkan untuk sesuatu yang agak "jelas", yang menunjukkan sesuatu yang tidak terjadi (yaitu mengecualikan karakter minus). Dengan hanya "angka", dan pedantically per definisi "digit" (satu 0 ... 9), itu tidak mungkin untuk memiliki setiap literal negatif (seperti -1). Yang mana, karena jenis default ditandatangani , bagaimanapun, jelas diperlukan, dan itu terbukti mungkin (dan diterima secara universal) juga.
Damon
1
Saya merujuk pada tata bahasa untuk integer-literal yang ditunjukkan pada tautan standar, yang tidak cocok 1-1. C ++ tidak memiliki literal bilangan bulat negatif. -1adalah ekspresi yang terdiri dari 1integer literal (dari tipe bertanda tangan) dan -operator minus unary. Lihat juga bagian "Catatan" di cppreference.com .
walnut
Memang benar bahwa tata bahasa tidak memilikinya, tetapi itu tidak penting. Berdasarkan kebutuhan dan menurut definisi, C ++ sangat memiliki literal negatif, karena kecuali jika Anda menambahkan secara eksplisit u, literal Anda, per definisi, ditandatangani. Jenis yang ditandatangani memiliki nilai negatif (sekitar 50% dari nilai yang mungkin negatif). Sangat disayangkan bahwa tata bahasa (untuk alasan yang saya tidak akan tahu) menyesatkan dengan cara ini, dan meskipun secara teknis (menurut tata bahasa) -1 adalah literal positif, dinegasikan, dengan segala cara lain tentu saja negatif harfiah. Sama seperti 3 + 4 adalah literal.
Damon
By the way - saya mencoba 0U. Masalah yang sama. Apa yang saya tidak coba adalah sebuah enumnilai. Mungkin yang bernama akan mengubah banyak hal. Saya akhirnya menulis ekspresi panjang dengan decltypedan remove_reference.
user1334767