Mencoba memahami templat dan pencarian nama

9

Saya mencoba memahami cuplikan kode berikut

Cuplikan # 1

template <typename T>
struct A
{
    static constexpr int VB = T::VD;
};

struct B : A<B>
{
};

Baik gcc9 atau dentang melemparkan kesalahan di sini.

Q. Mengapa kode ini dikompilasi? Bukankah kita instantiasi A<B>ketika mewarisi dari B? Tidak ada VD di B, jadi bukankah seharusnya kompiler membuat kesalahan di sini?

Cuplikan # 2

template <typename T>
struct A
{
    static constexpr auto AB = T::AD; // <- No member named AD in B
};

struct B : A<B>
{
    static constexpr auto AD = 0xD;
};

Dalam hal ini, gcc9 mengkompilasi dengan baik tetapi clang9 melempar kesalahan dengan mengatakan "Tidak ada anggota bernama AD dalam B".

P. Mengapa kompilasi dengan gcc9 / mengapa tidak dikompilasi dengan clang9?

Cuplikan # 3

template <typename T>
struct A
{
    using TB = typename T::TD;
};

struct B : A<B>
{
    using TD = int;
};

Di sini clang9 dan gcc9 melempar kesalahan. gcc9 mengatakan "penggunaan tidak valid tipe struct 'B' tidak lengkap.

Q. Jika struct B tidak lengkap di sini maka mengapa tidak lengkap di snippet # 2?

Compiler bendera yang digunakan: -std=c++17 -O3 -Wall -Werror. Terima kasih sebelumnya!!!

Efek Samping yang Bisa Berubah
sumber
@xception Bukankah struct Binstantiating Adengan B?
Efek Samping yang Dapat
clang9 melempar kesalahan dengan mengatakan "Tidak ada anggota bernama AD di B" . seperti Btidak lengkap ... Tetapi tidak yakin kapan anggota harus dipakai.
Jarod42
@MutableSideEffect oh ya, salahku, baca itu sebagai templat juga :(
xception
@ Jarod42 lalu mengapa gcc mengkompilasi dengan baik?
Efek Samping yang Dapat
1
Saya telah menandai pertanyaan ini sebagai "membutuhkan lebih banyak fokus" dan pertanyaan itu memang mengandung lebih dari satu pertanyaan (maka kesimpulan saya), jadi mengapa bendera saya salah?
Dominique

Jawaban:

4

Saya percaya ini pada dasarnya mendidih ke [temp.inst] / 2 (penekanan milik saya):

Instansiasi implisit dari spesialisasi templat kelas menyebabkan instantiasi implisit dari deklarasi, tetapi bukan definisi , argumen default, atau noexcept-specifier dari fungsi anggota kelas, kelas anggota, enumerasi anggota ruang lingkup, anggota data statis , templat anggota, dan teman; [...]

dan [temp.inst] / 9

Suatu implementasi tidak akan secara implisit memberi contoh […] anggota data statis dari templat kelas […] kecuali jika instantiasi tersebut diperlukan.

Kata-kata dalam standar tentang instantiasi template implisit membuat banyak detail terbuka untuk interpretasi. Secara umum, bagi saya tampaknya Anda tidak bisa mengandalkan bagian-bagian dari template yang tidak sedang dipakai kecuali spesifikasi secara eksplisit mengatakan demikian. Jadi:

Cuplikan # 1

Q. Mengapa kode ini dikompilasi? Bukankah kita membuat Instansiasi A ketika mewarisi dari B? Tidak ada VD di B, jadi bukankah seharusnya kompiler membuat kesalahan di sini?

Anda sedang instantiating A<B>. Tapi instantiating A<B>hanya instantiate deklarasi, bukan definisi anggota data statisnya. VBtidak pernah digunakan dengan cara yang akan membutuhkan definisi yang ada. Kompiler harus menerima kode ini.

Cuplikan # 2

P. Mengapa kompilasi dengan gcc9 / mengapa tidak dikompilasi dengan clang9?

Seperti yang ditunjukkan oleh Jarod42, deklarasi ABberisi tipe placeholder. Tampak bagi saya bahwa kata-kata dari standar tidak benar-benar jelas tentang apa yang seharusnya terjadi di sini. Apakah instantiasi deklarasi anggota data statis yang berisi tipe placeholder memicu pengurangan tipe placeholder dan, dengan demikian, merupakan penggunaan yang memerlukan definisi anggota data statis? Saya tidak dapat menemukan kata-kata dalam standar yang jelas akan mengatakan ya atau tidak pada itu. Jadi, saya akan mengatakan bahwa kedua interpretasi sama-sama valid di sini dan, dengan demikian, GCC dan dentang keduanya benar ...

Cuplikan # 3

Q. Jika struct B tidak lengkap di sini maka mengapa tidak lengkap di snippet # 2?

Sebuah jenis kelas hanya selesai pada titik di mana Anda mencapai penutupan }dari kelas-specifier [class.mem] / 6 . Dengan demikian, Btidak lengkap selama instantiasi implisit A<B>dalam semua cuplikan Anda. Hanya saja ini tidak relevan untuk Cuplikan # 1. Dalam Cuplikan # 2, dentang memang memberi Anda kesalahan No member named AD in Bsebagai hasilnya. Mirip dengan kasus Snippet # 2, saya tidak dapat menemukan kata-kata kapan tepatnya anggota alias deklarasi akan dipakai. Namun, tidak seperti definisi anggota data statis, tidak ada kata-kata di tempat untuk secara eksplisit mencegah instantiasi alias deklarasi anggota selama instantiasi implisit templat kelas. Jadi, saya akan mengatakan bahwa perilaku GCC dan dentang adalah interpretasi yang valid dari standar dalam kasus ini ...

Michael Kenzel
sumber
Terima kasih. Dalam hal ini, kami menginisialisasi anggota data statis di dalam tubuh. Apakah inisialisasi bagian dari deklarasi, atau bagian dari definisi anggota data statis? Saya mendapat kesan bahwa jika inisialisasi ada di dalam tubuh, maka itu adalah bagian dari pernyataan anggota data statis. Jika itu adalah bagian dari deklarasi, maka kutipan pertama Anda memerlukan instantiasi segera sebagai bagian dari instantiasi implisit sekitarnya dari templat kelas.
Johannes Schaub - litb
Saya melihat ke spec dan tampaknya ada perbedaan antara C ++ 14 dan C ++ 17 di sini. Di C ++ 14, constexpranggota data statis hanya deklarasi. C ++ 17 memperoleh inlinevariabel dan constexprmenyiratkan inline, dan ini membuat definisi anggota data statis dalam tubuh definisi.
Johannes Schaub - litb
eel.is/c++draft/dcl.spec.auto#4.sentence-2 mengatakan "Tipe variabel yang dideklarasikan menggunakan tipe placeholder disimpulkan dari penginisialisasi. Penggunaan ini diizinkan dalam deklarasi inisialisasi ([dcl. init]) dari suatu variabel. " Oleh karena itu saya berpendapat bahwa definisi anggota data statis diperlukan, karena berisi penginisialisasi.
Johannes Schaub - litb
@ JohannesSchaub-litb Terima kasih telah melihat ini! Saya telah bertanya-tanya tentang pertanyaan ini juga tetapi tidak dapat menemukan kata-kata yang akan meyakinkan. Mengenai hal inisialisasi vs definisi, pertimbangkan definisi fungsi anggota di dalam definisi templat kelas. Definisi semacam itu juga merupakan deklarasi dan tidak ada deklarasi lain. Namun, instantiasi implisit dari templat kelas hanya akan instantiate deklarasi tetapi bukan definisi fungsi anggota. Mengapa hal yang sama tidak berlaku untuk anggota data statis?
Michael Kenzel
jika tidak ada yang memerlukan definisi, definisi tersebut tidak dipakai. Tetapi dalam autokasus tersebut, aturan mengatakan bahwa deklarasi harus merupakan deklarasi inisialisasi. Ini hanya bisa terjadi jika diketahui bahwa deklarasi adalah definisi (sejauh yang saya tahu .. Saya sudah keluar dari tanah pengacara untuk sementara waktu). Di masa lalu, ada kasus serupa dan eel.is/c++draft/temp.inst#2.sentence-3 ditambahkan, di mana deklarasi itu definisi yang dipakai sebagai "dikenal sebagai definisi" tanpa sebenarnya instantiating definisi.
Johannes Schaub - litb