Mylący błąd szablonu


91

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ć templatesłowo kluczowe, aby kod był poprawny składniowo?


11
Czy próbowałeś wstawić go tam, gdzie wskazuje strzałka?
— Mike Seymour,

3
Podobnie jak to i to
— Prasoon Saurav

Odpowiedzi:


104

ISO C ++ 03 14.2 / 4:

Gdy nazwa specjalizacji szablonu członka pojawia się po. lub -> w wyrażeniu-przyrostku lub po specyfikatorze-nazwy-zagnieżdżonej-w-kwalifikowanym-id, a wyrażenie-przyrostka-kwalifikowanego-identyfikatora wyraźnie zależy od parametru-szablonu (14.6.2), nazwa szablonu elementu musi być poprzedzony szablonem słów kluczowych . W przeciwnym razie zakłada się, że nazwa określa nazwę niebędącą szablonem.

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 szablonu U, więc specjalizacja szablonu elementu członkowskiego musi być poprzedzona templatesłowem kluczowym.

Więc zmień t->f0<U>()na t->template f0<U>().


Co ciekawe, pomyślałem, że umieszczenie wyrażenia w nawiasach t->(f0<U>())naprawiłoby to, tak jak myślałem, że to f0<U>()

24
Czy mógłbyś skomentować, dlaczego tak się dzieje? Dlaczego C ++ miałby wymagać tego rodzaju składni?
— Ciekawy

2
Tak, to jest dziwne. Język może „wykryć”, że szablonowe słowo kluczowe musi być obecne. Jeśli może to zrobić, powinien po prostu „wstawić” słowo kluczowe do siebie.
— Enrico Borba

26

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ę, 0gdy pomijamy templatewcześniej, f<int()>ale 1podczas wstawiania. Zostawiam to jako ćwiczenie, aby dowiedzieć się, co robi kod.


3
To diabelski przykład!
— Matthieu M.

1
Nie mogę odtworzyć zachowania, które opisujesz w Visual Studio 2013. Zawsze wywołuje f<U>i zawsze drukuje 1, co dla mnie ma sens. Nadal nie rozumiem, dlaczego templatesłowo kluczowe jest wymagane i jakie ma znaczenie.
— Violet Giraffe

@Violet, kompilator VSC ++ nie jest zgodnym kompilatorem C ++. Potrzebne jest nowe pytanie, jeśli chcesz wiedzieć, dlaczego VSC ++ zawsze drukuje 1.
— Johannes Schaub - litb

1
Ta odpowiedź wyjaśnia, dlaczego templatejest 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.
— Johannes Schaub - litb

@ JohannesSchaub-litb: Dzięki, świetna odpowiedź. Okazuje się, że czytałem to już wcześniej, ponieważ zostało już przeze mnie przegłosowane. Najwyraźniej moja pamięć jest meh.
— Violet Giraffe

12

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 potrzebujesz typenamesł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 ".


1
Nigdy bym się tego nie domyślił ... ale dziękuję ;-). zawsze jest coś do nauczenia się o C ++!

3
Nawet z nieskończonym wyprzedzeniem nadal będziesz potrzebować template. Istnieją przypadki, w których zarówno z, jak i bez template, 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).
— Johannes Schaub - litb

@Johannes Schaub - litb: Tak, więc jest to bardziej problem przypisania spójnego znaczenia semantycznego wyrażeniu niż patrzenia w przyszłość.
— Doug

11

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.