2008-12-23 8 views
8

Visual Studio kompiluje ten kod dobrze, ale gcc pozwala tylko na kompilację bez operatora szablonu. Za pomocą operatora Szablon daje następujące błędy:Problem z szablonem GCC

Wiersz 29: błąd: oczekiwano `; ' przed "itrValue"

class Test 
{ 
    public: 

    Test& operator<<(const char* s) {return *this;} // not implemented yet 
    Test& operator<<(size_t  s) {return *this;} // not implemented yet 

    Test& operator<< (const std::list<const char*>& strList) 
    { 
    *this << "count=" << strList.size() << "("; 

    for (std::list<const char*>::const_iterator itrValue = strList.begin(); 
     itrValue != strList.end(); ++itrValue) 
    { 
     *this << " " << *itrValue; 
    } 

    *this << ")"; 

    return *this; 
    } 

    template <class T> 
    Test& operator<< (const std::list<T>& listTemplate) 
    { 
    *this << "count=" << listTemplate.size() << "("; 

    // this is line 28, the next line is the offending line 
    for (std::list<T>::const_iterator itrValue = listTemplate.begin(); 
     itrValue != listTemplate.end(); ++itrValue) 
    { 
     *this << " " << *itrValue; 
    } 

    *this << ")"; 

    return *this; 
    } 
}; 
+0

Nawet jeśli odpowiedź została już dostarczone przez Pieter, gdy mówimy o zachowaniach kompilatora ważne jest, aby określić dokładną wersję kompilatora ponieważ różne wersje mają różne zachowania (g ++ 4.0 narzeka na wiele innych rzeczy) –

Odpowiedz

16

GCC ma rację, const_iterator jest typem, a szablon zależne od operatora szablonu < <, trzeba poinformować kompilator to typ, a nie zmienna:

typename std::list<T>::const_iterator 
+0

Yup. Nie zawsze jest to oczywiste dla ludzi, którzy są nowi w tym ... ale to ta sama składnia, której można użyć do uzyskania statycznej zmiennej. "Typename" pozwala kompilatorowi wiedzieć, czego szukasz. –

+0

Tak, a interesujące jest to, że GCC miał tę funkcję (z określeniem const_iterator jest typem), zanim Visual Studio i później zdecydował się usunąć tę funkcję, aby kod kompilujący się na GCC był bardziej zgodny z normą. –

5

Aby zakończyć odpowiedź @Pieter, która jest poprawna, niektóre dodatkowe informacje na temat przetwarzania szablonów. Przede wszystkim szablony są kompilowane tylko wtedy, gdy są tworzone, więc jeśli nie stworzysz szablonu dla danego typu, kod nigdy nie zostanie skompilowany.

Teraz, gdy tworzysz instancję szablonu, następuje weryfikacja kodu szablonu w dwóch etapach. Najpierw szablon jest weryfikowany pod kątem poprawności niezależnie od tego, jaki jest typ instancji. Aby skontaktować się z prostszym na przykład zrozumieć:

#include "a.h" 

template <typename T> void f(T const &) 
{ 
    T::type x;  // is T::type a type? 
}; 

int main() 
{ 
    A a; 
    f(a); 
} 

W pierwszej fazie, szablon jest sprawdzane pod kątem poprawności składni bez rozważa to, co jest naprawdę. W tym czasie składnia A :: type może być typem o nazwie "type" lub może być statyczną zmienną o tej samej nazwie.

struct A { // version 1 
    typedef int type; 
}; 

struct A { // version 2 
    static std::string type; 
}; 
std::string A::type = "A"; 

W pierwszym przypadku typ jest rzeczywiście typem, w drugim nie. Teraz standardowe stwierdza, że ​​jeśli jest to naprawdę rodzaj następnie programista szablonu należy stwierdzić, tak aby poinformować kompilator ze składnią powyżej:

template <typename T> void f(T const & a) 
{ 
    typename T::type x; // define a variable x of type T::type 
} 

teraz, aby zakończyć przetwarzanie, kompilator musi sprawdzić, szablonu Kod jest nie tylko poprawny sam w sobie, ale że kiedy jest tworzony z określonym typem T, jest również poprawny. To właśnie wykonuje kompilator podczas drugiego etapu sprawdzania poprawności. Stosuje typ i ponownie sprawdza błędy.

W twoim przypadku jest to trochę bardziej kontrowersyjne, ponieważ każdy (ale kompilator) wie, że std :: list :: const_iterator jest typem dla dowolnego danego T. No cóż, to nie musi być. Z punktu widzenia języka, niektóre kody mogłyby zapewnić specjalizację szablonu dla określonego typu danych T, który różni się od ogólnego szablonu listy. Kompilator nie może wiedzieć, czy to możliwe.

Należy zauważyć, że byłoby strasznie źle wyspecyfikować szablon w przestrzeni nazw standardowych z czymś, co zmienia zachowanie w takim stopniu, jak na nowo definiuje typy iteratorów. Ale kompilator widzi std przestrzeń nazw tak jak każdą inną przestrzeń nazw i listę jak każdą inną szablonową klasę.

4

Myślę, że warto wam powiedzieć o innych ujednoznacznieniach. W przypadku typename już odpowiedziałem na inny here.

Drugi to szablon. Spójrz tutaj:

template<typename T> 
struct some { 
    template<int V> 
    struct other { 
     typedef int type; 
     static const int value = V; 
    }; 
}; 

template<typename V> 
void doit() { 
    typename some<V>::template other<42>::type * int_pointer; 
} 

Uwaga jak musieliśmy korzystać zarówno szablon i TypeName Disambiguations.Typename powiedział kompilator

The thing you access called ::type is indeed a type. Don't do multiplication, which would wrongly assume ::type is a static value (integer or something).

Szablon powiedział kompilator

The other<42> is a template used with the 42 argument. It's not a comparison using operator> and operator< of other with 42 and what follows (which would indeed be total nonsense).

Powiązane problemy