Kesalahan Template yang membingungkan


91

Saya telah bermain dengan clang beberapa saat, dan saya menemukan "test / SemaTemplate / dependent-template-recover.cpp" (dalam distribusi clang) yang seharusnya memberikan petunjuk untuk memulihkan dari kesalahan template.

Semuanya dapat dengan mudah dipreteli menjadi contoh minimal:

template<typename T, typename U, int N> struct X {
    void f(T* t)
    {
        // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
        t->f0<U>();
    }
};

Pesan kesalahan yang dihasilkan oleh dentang:

tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name
         t->f0<U>();
            ^
            template 
1 error generated.

... Tapi saya kesulitan memahami di mana tepatnya seseorang seharusnya memasukkan templatekata kunci agar kode tersebut benar secara sintaksis?


11
Apakah Anda mencoba memasukkannya ke tempat yang ditunjuk panah?
Mike Seymour

3
Mirip dengan ini dan ini
Prasoon Saurav

Jawaban:


104

ISO C ++ 03 14.2 / 4:

Ketika nama spesialisasi template anggota muncul setelahnya. atau -> dalam ekspresi-postfix, atau setelah penentu-nama-bersarang dalam id-kualifikasi, dan ekspresi-postfix atau id-yang memenuhi syarat secara eksplisit bergantung pada parameter-template (14.6.2), nama template anggota harus diawali dengan template kata kunci . Jika tidak, nama diasumsikan sebagai nama non-template.

In t->f0<U>(); f0<U>adalah spesialisasi template anggota yang muncul setelah ->dan yang secara eksplisit bergantung pada parameter template U, sehingga spesialisasi template anggota harus diawali dengan templatekata kunci.

Jadi ganti t->f0<U>()ke t->template f0<U>().


Menariknya, saya pikir meletakkan ekspresi dalam tanda kurung: t->(f0<U>())akan memperbaikinya, karena saya pikir itu akan dimasukkan f0<U>()ke dalam ekspresi mandiri ... yah, saya pikir salah, tampaknya ...

24
Bisakah Anda berkomentar mengapa hal ini terjadi? Mengapa C ++ memerlukan sintaks semacam ini?
Curious

2
Ya ini aneh. Bahasa tersebut dapat "mendeteksi" bahwa kata kunci template harus ada. Jika dapat melakukannya maka itu hanya harus "memasukkan" kata kunci di sana sendiri.
Enrico Borba

26

Selain poin yang dibuat orang lain, perhatikan bahwa terkadang compiler tidak dapat mengambil keputusan dan kedua interpretasi dapat menghasilkan program alternatif yang valid saat membuat instance

#include <iostream>

template<typename T>
struct A {
  typedef int R();

  template<typename U>
  static U *f(int) { 
    return 0; 
  }

  static int f() { 
    return 0;
  }
};

template<typename T>
bool g() {
  A<T> a;
  return !(typename A<T>::R*)a.f<int()>(0);
}


int main() {
  std::cout << g<void>() << std::endl;
}

Ini dicetak 0saat menghilangkan templatesebelumnya f<int()>tetapi 1saat memasukkannya. Saya membiarkannya sebagai latihan untuk mencari tahu apa yang dilakukan kode itu.


3
Nah, itu contoh yang jahat!
Matthieu M.

1
Saya tidak bisa mereproduksi perilaku yang Anda gambarkan dalam Visual Studio 2013. Itu selalu memanggil f<U>dan selalu mencetak 1, yang sangat masuk akal bagi saya. Saya masih tidak mengerti mengapa templatekata kunci itu diperlukan dan apa bedanya.
Violet Giraffe

@Violet, kompilator VSC ++ bukan kompilator C ++ yang patuh. Sebuah pertanyaan baru diperlukan jika Anda ingin tahu mengapa VSC ++ selalu mencetak 1.
Johannes Schaub - litb

1
Jawaban ini menjelaskan mengapa templatediperlukan: stackoverflow.com/questions/610245/… tanpa hanya mengandalkan istilah standar yang sulit dipahami. Mohon laporkan jika ada jawaban yang masih membingungkan.
Johannes Schaub - litb

@ JohannesSchaub-litb: Terima kasih, jawaban yang bagus. Ternyata, saya sudah membacanya sebelumnya karena sudah saya beri suara positif. Ternyata, ingatanku adalah meh.
Violet Giraffe

12

Masukkan tepat sebelum titik tanda sisipannya:

template<typename T, typename U, int N> struct X {
     void f(T* t)
     {
        t->template f0<U>();
     }
};

Edit: alasan aturan ini menjadi lebih jelas jika Anda berpikir seperti seorang compiler. Kompiler umumnya hanya melihat ke depan satu atau dua token sekaligus, dan umumnya tidak "melihat ke depan" ke sisa ekspresi. [Sunting: lihat komentar] Alasan untuk kata kunci ini sama dengan mengapa Anda memerlukan typenamekata kunci untuk menunjukkan nama tipe dependen: ini memberi tahu kompiler "hei, pengenal yang akan Anda lihat adalah nama template, bukan nama anggota data statis diikuti dengan tanda kurang dari ".


1
Saya tidak akan pernah bisa menebak itu ... tapi terima kasih ;-). jelas selalu ada sesuatu untuk dipelajari tentang C ++!

3
Bahkan dengan pandangan ke depan yang tak terbatas, Anda masih perlu template. Ada kasus di mana dengan dan tanpa templateakan menghasilkan program yang valid dengan perilaku berbeda. Jadi ini bukan hanya masalah sintaksis ( t->f0<int()>(0)valid secara sintaksis untuk versi daftar argumen kurang dari dan templat).
Johannes Schaub - litb

@Johannes Schaub - litb: Benar, jadi ini lebih merupakan masalah menetapkan makna semantik yang konsisten pada ekspresi, daripada melihat ke depan.
Doug

11

Kutipan dari C ++ Templates

Konstruksi .template Masalah yang sangat mirip ditemukan setelah pengenalan nama jenis. Perhatikan contoh berikut menggunakan tipe bitet standar:

template<int N> 
void printBitset (std::bitset<N> const& bs) 
{ 
    std::cout << bs.template to_string<char,char_traits<char>, 
                                       allocator<char> >(); 
} 

Konstruksi aneh dalam contoh ini adalah .template. Tanpa penggunaan ekstra dari template, compiler tidak akan mengetahui bahwa less-than token (<) yang mengikuti tidak benar-benar "kurang dari" tetapi awal dari daftar argumen template. Perhatikan bahwa ini adalah masalah hanya jika konstruksi sebelum periode bergantung pada parameter template. Dalam contoh kita, parameter bs bergantung pada parameter template N.

Kesimpulannya, notasi .template (dan notasi serupa seperti -> template) harus digunakan hanya di dalam template dan hanya jika mengikuti sesuatu yang bergantung pada parameter template.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.