Bawiłem się przez jakiś czas z clang i natknąłem się na "test / SemaTemplate / dependent-template-recovery.cpp" (w dystrybucji clang), który ma dostarczać wskazówek, jak odzyskać dane po błędzie szablonu.
Całość można łatwo sprowadzić do minimalnego przykładu:
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>();
}
};
Komunikat o błędzie wygenerowany przez clang:
tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name
t->f0<U>();
^
template
1 error generated.
... Ale trudno mi zrozumieć, gdzie dokładnie należy wstawić template
słowo kluczowe, aby kod był poprawny składniowo?
Odpowiedzi:
ISO C ++ 03 14.2 / 4:
In
t->f0<U>();
f0<U>
to specjalizacja szablonu elementu członkowskiego, która pojawia się po->
i która wyraźnie zależy od parametru szablonuU
, więc specjalizacja szablonu elementu członkowskiego musi być poprzedzonatemplate
słowem kluczowym.Więc zmień
t->f0<U>()
nat->template f0<U>()
.źródło
t->(f0<U>())
naprawiłoby to, tak jak myślałem, że tof0<U>()
Oprócz uwag innych, zwróć uwagę, że czasami kompilator nie mógł się zdecydować i obie interpretacje mogą przy tworzeniu instancji dostarczyć alternatywne prawidłowe programy
#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; }
Wydrukuje się,
0
gdy pomijamytemplate
wcześniej,f<int()>
ale1
podczas wstawiania. Zostawiam to jako ćwiczenie, aby dowiedzieć się, co robi kod.źródło
f<U>
i zawsze drukuje1
, co dla mnie ma sens. Nadal nie rozumiem, dlaczegotemplate
słowo kluczowe jest wymagane i jakie ma znaczenie.template
jest potrzebna: stackoverflow.com/questions/610245/ ... bez polegania wyłącznie na standardowych terminach, które są trudne do zrozumienia. Proszę zgłosić, jeśli cokolwiek w tej odpowiedzi jest nadal niejasne.Wstaw go tuż przed punktem, w którym znajduje się daszek:
template<typename T, typename U, int N> struct X { void f(T* t) { t->template f0<U>(); } };
Edycja: przyczyna tej reguły staje się jaśniejsza, jeśli myślisz jak kompilator.
Kompilatory generalnie patrzą w przód tylko na jeden lub dwa tokeny na raz i generalnie nie „patrzą w przyszłość” na resztę wyrażenia.[Edytuj: patrz komentarz] Powód słowa kluczowego jest taki sam, jak dlaczego potrzebujesztypename
słowa kluczowego do wskazywania nazw typów zależnych: mówi kompilatorowi „hej, identyfikator, który zobaczysz, to nazwa szablonu, a nie nazwa statycznego elementu danych, po którym następuje znak mniejszości ".źródło
template
. Istnieją przypadki, w których zarówno z, jak i beztemplate
, dają prawidłowe programy o różnym zachowaniu. Jest to więc nie tylko problem syntaktyczny (t->f0<int()>(0)
jest poprawny składniowo zarówno dla wersji less-than, jak i listy argumentów szablonu).Fragment z szablonów C ++
Konstrukcja .template Bardzo podobny problem został wykryty po wprowadzeniu typename. Rozważmy następujący przykład przy użyciu standardowego typu zestawu bitów:
template<int N> void printBitset (std::bitset<N> const& bs) { std::cout << bs.template to_string<char,char_traits<char>, allocator<char> >(); }
Dziwną konstrukcją w tym przykładzie jest .template. Bez tego dodatkowego użycia szablonu kompilator nie wie, że następujący po nim token mniejszy niż (<) nie jest tak naprawdę „mniejszym niż”, ale początkiem listy argumentów szablonu. Zauważ, że jest to problem tylko wtedy, gdy konstrukcja przed okresem zależy od parametru szablonu. W naszym przykładzie parametr bs zależy od parametru szablonu N.
Podsumowując, notacja .template (i podobne notacje, takie jak -> szablon) powinny być używane tylko wewnątrz szablonów i tylko wtedy, gdy są zgodne z czymś, co zależy od parametru szablonu.
źródło