C ++ - Mengapa kata kunci 'templat' diperlukan di sini?

9

Saya memiliki kode berikut:

template <typename TC>
class C
{
    struct S
    {
        template <typename TS>
        void fun() const
        {}
    };

    void f(const S& s)
    {
        s.fun<int>();
    }
};

// Dummy main function
int main()
{
    return 0;
}

Saat membuat ini dengan gcc 9.2 dan dentang (9.0), saya mendapatkan kesalahan kompilasi karena templatekata kunci diperlukan untuk memohon fun. Dentang menunjukkan:

error: use 'template' keyword to treat 'fun' as a dependent template name
        s.fun<int>();
          ^
          template 

Saya tidak mengerti mengapa kompiler berpikir funadalah nama dependen dalam konteks f, karena fbukan template itu sendiri. Jika saya berubah Cmenjadi kelas reguler alih-alih templat, kesalahan hilang; Namun, saya tidak melihat mengapa harus ada kesalahan sejak awal karena keduanya Stidak fbergantung TC.

Anehnya, MSVC 19.22 mengkompilasi ini dengan baik.


catatan

Sebelum pemungutan suara ditutup sebagai dupe dari Di mana dan mengapa saya harus meletakkan kata kunci "templat" dan "ketik nama"? tolong pertimbangkan ini adalah kasus khusus di mana bahkan jika Smemang nama dependen, dalam konteks fitu tidak akan tergantung jika bukan karena fakta bahwa mereka adalah anggota Instansiasi saat ini.

Martin
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Bhargav Rao

Jawaban:

10

Pertimbangkan :

template<typename T>
struct C
{
    struct S
    {
        int a = 99;
    };

    void f(S s, int i)
    {
        s.a<0>(i);
    }
};

template<>
struct C<long>::S
{
    template<int>
    void a(int)
    {}
};

int main()
{
    C<int>{}.f({}, 0); // #1
    C<long>{}.f({}, 0); // #2
}

s.a<0>(i)diuraikan sebagai ekspresi yang berisi dua operasi perbandingan <dan >, dan ini baik untuk # 1 tetapi gagal untuk # 2.

Jika ini diubah untuk s.template a<0>(i)kemudian # 2 adalah OK dan # 1 gagal. Dengan demikian templatekata kunci tidak pernah berlebihan di sini.

MSVC mampu menafsirkan ekspresi s.a<0>(i)dua arah dalam program yang sama. Tetapi ini tidak benar menurut Standar; setiap ekspresi seharusnya hanya memiliki satu parse untuk ditangani oleh kompiler.

ecatmur
sumber
Saya masih belum sepenuhnya memahami hal ini. Contoh Anda menunjukkan bahwa dalam hal ini Anda menggunakan spesialisasi Catau yang lain, tetapi Anda tidak dapat membuat instantiate keduanya. Kata templatekunci di sini adalah IMHO yang tidak dibutuhkan, karena yang Sdipilih tergantung pada yang CAnda instantiate. Tanpa templatekata kunci, Anda dapat meng-instantiate keduanya, dan perilaku f akan berbeda untuk setiap instance.
Martin
2
@Martin intinya adalah bahwa setiap token seharusnya hanya memiliki satu peran sintaksis dalam file sumber. Misalnya, token <tidak menjadi operator perbandingan dalam satu instantiasi templat dan braket sudut pembuka di instantiasi lain. Ini untuk memastikan bahwa kompiler dapat mem-parsing templat ke AST (dengan placeholder untuk tipe templat).
ecatmur
Itu masuk akal. Terima kasih!
Martin
7

funmungkin atau mungkin bukan fungsi templat (atau mungkin tidak ada sama sekali) tergantung pada parameter templat class C.

Itu karena Anda dapat berspesialisasi S(tanpa berspesialisasi C):

template <> struct C<int>::S {};

Karena kompiler ingin tahu apakah funtemplate atau tidak ketika pertama kali melihat class C(sebelum mengganti parameter template), templatediperlukan.

HolyBlackCat
sumber
1
keberatan ... ditiup ...
bolov
Tindak lanjut dari hal ini adalah apakah definisi baru ini Sdapat diakses oleh f. Jika mereka tidak bisa, pembatasan ini tidak masuk akal karena ftidak akan dapat melihatnya lagi.
Martin
@ Martin Dengan GCC, Dentang , dan MSVC keduanya fmenemukannya.
HolyBlackCat
@bolov Ya, Anda dapat mengkhususkan banyak hal yang berbeda .
HolyBlackCat