Pertimbangkan program ini:
#include <cstdint>
using my_time_t = uintptr_t;
int main() {
const my_time_t t = my_time_t(nullptr);
}
Gagal mengompilasi dengan msvc v19.24:
<source>(5): error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'my_time_t'
<source>(5): note: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type
<source>(5): error C2789: 't': an object of const-qualified type must be initialized
<source>(5): note: see declaration of 't'
Compiler returned: 2
tapi dentang (9.0.1) dan gcc (9.2.1) "makan" kode ini tanpa kesalahan.
Saya suka perilaku MSVC, tetapi apakah itu dikonfirmasi oleh standar? Dengan kata lain apakah bug di dentang / gcc atau dimungkinkan untuk menafsirkan standar bahwa ini adalah perilaku yang benar dari gcc / dentang?
Jawaban:
Menurut pendapat saya MSVC tidak berperilaku sesuai standar.
Saya mendasarkan jawaban ini pada C ++ 17 (draft N4659), tetapi C ++ 14 dan C ++ 11 memiliki kata-kata yang setara.
my_time_t(nullptr)
adalah ekspresi-postfix dan karenamy_time_t
merupakan tipe dan(nullptr)
merupakan ekspresi tunggal dalam daftar initializer yang di-kurung, itu persis sama dengan ekspresi cast yang eksplisit. ( [expr.type.conv] / 2 )Pemeran eksplisit mencoba beberapa pemeran C ++ spesifik yang berbeda (dengan ekstensi), khususnya juga
reinterpret_cast
. ( [expr.cast] /4.4 ) Gips yang dicoba sebelumnyareinterpret_cast
adalahconst_cast
danstatic_cast
(dengan ekstensi dan juga dalam kombinasi), tetapi tidak ada yang dapat dilemparkanstd::nullptr_t
ke tipe integral.Tetapi
reinterpret_cast<my_time_t>(nullptr)
harus berhasil karena [expr.reinterpret.cast] / 4 mengatakan bahwa nilai tipestd::nullptr_t
dapat dikonversi ke tipe integral seolah-olah olehreinterpret_cast<my_time_t>((void*)0)
, yang mungkin karenamy_time_t = std::uintptr_t
harus merupakan tipe yang cukup besar untuk mewakili semua nilai pointer dan dalam kondisi ini paragraf standar yang sama memungkinkan konversivoid*
ke tipe integral.Sangat aneh bahwa MSVC mengizinkan konversi jika notasi cor daripada notasi fungsional digunakan:
sumber
static_cast
secara khusus ada beberapa kasus yang dimaksudkan untuk menjebak tangga cor gaya-C (misalnya, cor gaya-C ke basis yang ambigu lebih burukstatic_cast
daripadareinterpret_cast
), tetapi tidak ada yang berlaku di sini.my_time_t(nullptr)
secara definisi sama(my_time_t)nullptr
, jadi MSVC tentu salah menerima dan menolak yang lain.Meskipun saya tidak dapat menemukan penyebutan eksplisit dalam Standar Draf C ++ Kerja ini (mulai 2014) bahwa konversi dari
std::nullptr_t
ke tipe integral dilarang, ada juga tidak menyebutkan bahwa konversi semacam itu diperbolehkan!Namun, kasus konversi dari
std::nullptr_t
kebool
adalah eksplisit disebutkan:Lebih jauh, satu - satunya tempat dalam draft dokumen ini di mana konversi dari
std::nullptr_t
ke tipe integral disebutkan, ada di bagian "reinterpret_cast":Jadi, dari dua pengamatan ini, salah satu bisa (IMHO) cukup dugaan bahwa
MSVC
compiler benar.EDIT : Namun, penggunaan "notasi fungsional" Anda sebenarnya dapat menyarankan sebaliknya! The
MSVC
compiler tidak memiliki masalah menggunakan cor C-gaya, misalnya:tetapi (seperti dalam kode Anda), ia mengeluh tentang ini:
Namun, dari Draf Standar yang sama:
"Ekspresi pemeran yang sesuai (5,4)" dapat merujuk pada pemeran gaya-C.
sumber
Semua adalah konforman standar (ref. Draft n4659 untuk C ++).
nullptr
didefinisikan dalam [lex.nullptr] sebagai:Sekalipun nota non normatif, nota ini memperjelas bahwa untuk standar,
nullptr
diharapkan dikonversi ke nilai pointer nol .Kami kemudian menemukan di [conv.ptr]:
Di sini sekali lagi apa yang diperlukan oleh standar adalah yang
0
dapat dikonversi menjadistd::nullptr_t
dan yangnullptr
dapat dikonversi ke jenis pointer apa pun.Bacaan saya adalah bahwa standar tidak memiliki persyaratan apakah
nullptr
dapat langsung dikonversi ke tipe integral atau tidak. Sejak saat itu:void *
konversi perantara terlibat.sumber
nullptr
bukan karena memiliki tipe non-integralstd::nullptr_t
. 0 dapat dikonversi kestd::nullptr_t
nilai, tetapi tidak ke literalnullptr
. Ini semua disengaja,std::nullptr_t
adalah jenis yang lebih terbatas untuk mencegah konversi yang tidak diinginkan.