Apakah perbedaan dua instance constexpr dari pointer __func__ masih merupakan constexpr?

14

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_todapat 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__);
}
Ayxan
sumber
Dari Function_definition , __func__as-if static const char __func__[] = "function-name";dan yang setara diterima Demo ...
Jarod42
Menariknya, ini berfungsi jika Anda menginisialisasi variabel constexpr dengan __func__dan menggunakannya di static_assert ...
florestan
@ Jarod42 Jadi ini bug di Dentang?
Ayxan
@florestan menyukai ini ? Itu tidak akan dikompilasi dengan Dentang juga. Contoh 2 dan 3 saya dalam pertanyaan adalah cara Anda menyebutkan. Satu kompilasi, yang lain tidak.
Ayxan
1
Lihat juga CWG1962 , yang mungkin menghapus __func__seluruhnya dari evaluasi constexpr.
Davis Herring

Jawaban:

13

__func__di C ++ adalah pengidentifikasi. Secara khusus, referensi objek tertentu. Dari [dcl.fct.def.general] / 8 :

Variabel fungsi-lokal yang telah ditetapkan _­_­func_­_­didefinisikan sebagai seolah-olah definisi formulir

static const char __func__[] = "function-name";

telah disediakan, di mana fungsi-nama adalah string yang ditentukan implementasi. Tidak ditentukan apakah variabel tersebut memiliki alamat yang berbeda dari objek lain dalam program.

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 :

Untuk template-parameter non-tipe referensi atau tipe pointer, nilai ekspresi konstan tidak boleh merujuk (atau untuk tipe pointer, tidak boleh menjadi alamat):

...

  • _­_­func_­_variabel yang sudah ditentukan sebelumnya .

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.

Nicol Bolas
sumber
Jadi, berbicara dengan tegas __func__dapat menjadi ekspresi konstan dalam semua kasus dalam pertanyaan saya, bukan? Jadi kode harus dikompilasi.
Ayxan
Bagaimana dengan "Tidak ditentukan apakah variabel tersebut memiliki alamat yang berbeda dari objek lain dalam program ini." bagian? Perilaku yang tidak ditentukan berarti non-determinisme dalam perilaku mesin abstrak. Bisakah itu bermasalah untuk evaluasi constexpr? Bagaimana jika untuk kemunculan pertama __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!
Johannes Schaub - litb
@ JohannesSchaub-litb: " Bagaimana dengan" Tidak ditentukan apakah variabel seperti itu memiliki alamat yang berbeda dari objek lain dalam program ini. "Bagian? " Bagaimana dengan itu? __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.
Nicol Bolas
@Nicol yang merujuk ke objek yang sama. Tapi objek itu mungkin sekaligus memiliki alamat yang sama dengan objek lain. Dan di saat lain belum. Saya tidak mengatakan itu masalah, tapi saya hanya mengingatkan semua orang tentang kemungkinan ini. Dan setelah semua, saya mungkin juga salah, jadi saya juga mengatakan ini dengan harapan diperbaiki atau dikonfirmasi.
Johannes Schaub - litb
@ JohannesSchaub-litb: " Tapi objek itu mungkin pada satu saat memiliki alamat yang sama dengan objek lain. " Itu tidak diizinkan di bawah model objek C ++. Dua objek, tidak satu pun bersarang di dalam yang lain, tidak bisa keduanya berada dalam masa hidup mereka di penyimpanan yang sama pada saat yang sama. Dan objek yang dimaksud memiliki durasi penyimpanan statis, jadi kecuali jika Anda menggunakan penempatan new, objek tersebut tidak akan bergerak hingga program berakhir.
Nicol Bolas