Apakah ini C ++ yang valid?
int main() {
constexpr auto sz = __func__ - __func__;
return sz;
}
GCC dan MSVC berpikir tidak apa-apa, Clang berpikir itu tidak: Compiler Explorer .
Semua kompiler setuju bahwa ini OK: Kompiler Explorer .
int main() {
constexpr auto p = __func__;
constexpr auto p2 = p;
constexpr auto sz = p2 - p;
return sz;
}
Dentang lagi tidak suka yang ini, tapi yang lain tidak masalah dengan itu: Compiler Explorer
int main() {
constexpr auto p = __func__;
constexpr auto p2 = __func__;
constexpr auto sz = p2 - p;
return sz;
}
Ada apa di sini? Saya pikir aritmatika pada pointer yang tidak berhubungan adalah perilaku yang tidak terdefinisi tetapi __func__
mengembalikan pointer yang sama, bukan? Saya tidak yakin, jadi saya pikir saya bisa mengujinya. Jika saya ingat dengan benar, std::equal_to
dapat membandingkan pointer yang tidak terkait tanpa perilaku yang tidak terdefinisi:
#include <functional>
int main() {
constexpr std::equal_to<const char*> eq{};
static_assert(eq(__func__, __func__));
}
Dentang berpikir eq(__func__, __func__)
bukan ekspresi konstan, meskipun std::equal_to::operator()
adalah constexpr . Kompiler lain tidak mengeluh: Kompiler Explorer
Dentang juga tidak akan mengkompilasi yang ini. Keluhan yang __func__ == __func__
bukan ekspresi konstan: Kompiler Explorer
int main() {
static_assert(__func__ == __func__);
}
__func__
as-ifstatic const char __func__[] = "function-name";
dan yang setara diterima Demo ...__func__
dan menggunakannya di static_assert ...__func__
seluruhnya dari evaluasi constexpr.Jawaban:
__func__
di C ++ adalah pengidentifikasi. Secara khusus, referensi objek tertentu. Dari [dcl.fct.def.general] / 8 :Sebagai variabel standar fungsi-lokal , definisi ini (seolah-olah) muncul di awal blok fungsi. Dengan demikian, setiap penggunaan di
__func__
dalam blok itu akan merujuk ke variabel itu.Adapun bagian "objek lain", variabel mendefinisikan objek.
__func__
memberi nama objek yang didefinisikan oleh variabel itu. Oleh karena itu, dalam suatu fungsi, semua penggunaan__func__
nama variabel yang sama. Apa yang tidak terdefinisi adalah apakah variabel itu adalah objek yang berbeda dari objek lain.Artinya, jika Anda berada dalam suatu fungsi bernama
foo
, dan Anda menggunakan literal di"foo"
tempat lain dalam masalah, itu tidak dilarang untuk implementasi untuk memiliki variabel__func__
juga menjadi objek yang sama dengan yang"foo"
dikembalikan literal . Artinya, standar tidak mengharuskan setiap fungsi yang__func__
muncul harus menyimpan data terpisah dari string literal itu sendiri.Sekarang, aturan "seolah-olah" C ++ memungkinkan implementasi menyimpang dari ini, tetapi mereka tidak dapat melakukannya dengan cara yang dapat dideteksi. Jadi, sementara variabel itu sendiri mungkin atau mungkin tidak memiliki alamat yang berbeda dari objek lain, penggunaan
__func__
fungsi yang sama harus berperilaku seolah-olah mereka merujuk ke objek yang sama.Dentang tampaknya tidak menerapkan
__func__
cara ini. Tampaknya mengimplementasikannya seolah-olah mengembalikan string literal nilai awal dari nama fungsi. Dua literal string yang berbeda tidak harus merujuk ke objek yang sama, jadi mengurangi pointer ke mereka adalah UB. Dan perilaku yang tidak terdefinisi dalam konteks ekspresi konstan tidak terbentuk.Satu-satunya hal yang membuat saya ragu untuk mengatakan bahwa Dentang adalah 100% salah di sini adalah [temp.arg.nontype] / 2 :
Lihat, ini tampaknya memungkinkan beberapa kecurangan oleh implementasi. Artinya, sementara
__func__
secara teknis bisa menjadi ekspresi konstan, Anda tidak bisa menggunakannya dalam parameter template. Ini diperlakukan seperti string literal, meskipun secara teknis variabel.Jadi pada tingkat tertentu, saya akan mengatakan bahwa standar berbicara dari kedua sisi mulutnya.
sumber
__func__
dapat menjadi ekspresi konstan dalam semua kasus dalam pertanyaan saya, bukan? Jadi kode harus dikompilasi.__func__
alamat sama dengan objek lain, dan pada kemunculan kedua__func__
bukan? Memang, itu tidak berarti bahwa alamatnya berbeda antara dua contoh, tapi saya masih bingung!__func__
bukan makro; itu adalah pengidentifikasi yang menamai variabel tertentu dan karenanya objek tertentu. Oleh karena itu, setiap penggunaan__func__
fungsi yang sama harus menghasilkan nilai yang mengacu pada objek yang sama. Atau lebih tepatnya, itu tidak dapat diimplementasikan sedemikian rupa sehingga ini tidak akan terjadi.new
, objek tersebut tidak akan bergerak hingga program berakhir.