Apakah Dentang benar untuk menolak kode di mana kelas bersarang dari templat kelas didefinisikan hanya melalui spesialisasi?

17

Diberikan templat kelas berikut:

template<typename T>
struct Outer
{
    struct Inner;

    auto f(Inner) -> void;
};

kami mendefinisikan Innersecara terpisah untuk setiap spesialisasi Outer:

template<>
struct Outer<int>::Inner {};

template<>
struct Outer<double>::Inner {};

dan kemudian mendefinisikan fungsi anggota fsekali untuk semua spesialisasi Outer:

auto Outer<T>::f(Inner) -> void
{

}

tapi Dentang (9.0.0) mengeluh:

error: variable has incomplete type 'Outer::Inner'

auto Outer<T>::f(Inner) -> void

                      ^

Kami dapat menghindari kesalahan kompilator dengan juga memberikan definisi Inneruntuk semua spesialisasi lainnya dari Outer:

template<typename T>
struct Outer<T>::Inner {};

atau dengan mendefinisikan fsecara terpisah untuk setiap spesialisasi:

template<>
auto Outer<int>::f(Inner) -> void
{

}

template<>
auto Outer<double>::f(Inner) -> void
{

}

Baik GCC dan MSVC menerima kode awal, yang menimbulkan pertanyaan; apakah ini bug Dentang atau hanya implementasi yang sesuai dari ketiganya?

Coba di Compiler Explorer

invexed
sumber
Spesialisasi batin tidak relevan, menghapusnya tidak mengubah hasil kompilasi.
n. 'kata ganti' m.
@ n.'pronouns'm. Saya tidak yakin apa yang Anda maksud. Keduanya menambahkan definisi Inneruntuk semua spesialisasi lainnya dan mendefinisikan fsecara terpisah untuk setiap spesialisasi menyelesaikan kesalahan kompilasi.
invexed
Mari kita baca lagi: menghapusnya tidak mengubah hasil kompilasi . Tidak menambahkan, menghapus. gcc clang
n. 'kata ganti' m.
@ n.'pronouns'm. Saya mengerti maksud Anda sekarang, tetapi itu masih merupakan komentar aneh. Inti dari pertanyaan saya Inneradalah yang dilaporkan sebagai tipe yang tidak lengkap meskipun ada definisi untuk setiap spesialisasi Outeryang disediakan. Jelas Innerakan (dengan benar) menjadi tipe yang tidak lengkap jika Anda menghapus definisinya.
invexed
"Jelas batin akan (benar) menjadi tipe lengkap jika Anda menghapus definisi (s)." Ada yang "bukan ckear sama sekali Sebuah spesialisasi adalah template benar-benar terpisah dan tidak mempengaruhi template utama sama sekali..
n. 'kata ganti' m.

Jawaban:

4

Saya percaya Dentang salah untuk menolak kode Anda. Kita harus bertanya pada diri sendiri, bagaimana deklarasi dan definisi fungsi Anda dibandingkan

auto f(typename T::Inner) -> void;

// ...

template<typename T>
auto Outer<T>::f(typename T::Inner) -> void
{ }

Dalam contoh ini, T::Innerjelas merupakan tipe dependen. Jadi Dentang mungkin tidak menganggap itu tidak lengkap sampai Instansiasi. Apakah hal yang sama berlaku pada contoh Anda? Saya akan mengatakan demikian. Karena kami memiliki ini dalam standar:

[temp.dep.type]

5 Nama adalah anggota instantiasi saat ini jika ya

  • Nama yang tidak memenuhi syarat yang, ketika dilihat, mengacu pada setidaknya satu anggota kelas yang merupakan instance saat ini atau kelas dasar yang tidak bergantung padanya. [Catatan: Ini hanya dapat terjadi ketika mencari nama dalam lingkup yang dilampirkan oleh definisi templat kelas. - catatan akhir]
  • ...

Sebuah nama adalah anggota dependen dari instantiasi saat ini jika itu adalah anggota instantiasi saat ini yang, ketika dilihat, merujuk pada setidaknya satu anggota kelas yang merupakan instantiasi saat ini.

9 Suatu tipe tergantung jika itu

  • ...
  • anggota dari spesialisasi yang tidak diketahui,
  • kelas bersarang atau enumerasi yang merupakan anggota dependen dari Instansiasi saat ini,
  • ...

Jadi peluru pertama dalam paragraf 9 mencakup kasus ini typename T::Inner. Itu adalah tipe dependen.

Sementara kasing Anda dilindungi oleh peluru kedua. Outer::Inneradalah nama yang ditemukan di instance saat ini Outer, apalagi itu ditemukan di dalam Outerdirinya sendiri, dan bukan di kelas dasar. Itu membuatnya menjadi anggota tergantung dari Instansiasi saat ini. Nama ini merujuk ke kelas bersarang. Yang berarti semua kondisi dalam peluru kedua berlaku, sehingga membuat Outer::Innertipe dependen juga!

Karena kita memiliki tipe dependen dalam kedua kasus ini, kompiler harus memperlakukannya sama seperti tipe dependen. Kesimpulan saya adalah bahwa GCC dan MSVC benar.

StoryTeller - Unslander Monica
sumber
Bug dilaporkan . Terima kasih.
invexed