2008-08-14 10 views
32

Poniższy kod nie skompilować z gcc, ale robi z Visual Studio:GCC problem: przy użyciu członka klasy bazowej, która zależy od szablonu argumentu

template <typename T> class A { 
public: 
    T foo; 
}; 

template <typename T> class B: public A <T> { 
public: 
    void bar() { cout << foo << endl; } 
}; 

pojawia się błąd:

test.cpp: In member function ‘void B::bar()’:

test.cpp:11: error: ‘foo’ was not declared in this scope

Ale tak powinno być! Jeśli zmienię bar do

void bar() { cout << this->foo << endl; } 

to robi kompilacji, ale nie sądzę, muszę to zrobić. Czy jest coś w oficjalnej specyfikacji C++, którą GCC podąża tutaj, czy jest to tylko dziwactwo?

+0

Zobacz [W klasie pochodzącej z szablonu, dlaczego muszę kwalifikować nazwy członków klasy podstawowej za pomocą "this->" wewnątrz funkcji członka?] (Http://stackoverflow.com/questions/7908248/in-a- Templated-derived-class-why-do-i-need-to-qualify-base-class-member-names-w) – curiousguy

Odpowiedz

10

Ta zmieniona w gcc-3.4. Parser C++ stał się znacznie bardziej rygorystyczny w tym wydaniu - według specyfikacji, ale nadal irytujący dla osób ze starszymi lub wieloplatformowymi bazami kodu.

18

Wow. C++ nigdy nie przestaje mnie zaskakiwać swoją niesamowitością.

In a template definition, unqualified names will no longer find members of a dependent base (as specified by [temp.dep]/3 in the C++ standard). For example,

template <typename T> struct B { 
    int m; 
    int n; 
    int f(); 
    int g(); 
}; 
int n; 
int g(); 
template <typename T> struct C : B<T> { 
    void h() 
    { 
    m = 0; // error 
    f(); // error 
    n = 0; // ::n is modified 
    g(); // ::g is called 
    } 
}; 

You must make the names dependent, e.g. by prefixing them with this->. Here is the corrected definition of C::h,

template <typename T> void C<T>::h() 
{ 
    this->m = 0; 
    this->f(); 
    this->n = 0 
    this->g(); 
} 

As an alternative solution (unfortunately not backwards compatible with GCC 3.3), you may use using declarations instead of this->:

template <typename T> struct C : B<T> { 
    using B<T>::m; 
    using B<T>::f; 
    using B<T>::n; 
    using B<T>::g; 
    void h() 
    { 
    m = 0; 
    f(); 
    n = 0; 
    g(); 
    } 
}; 

to tylko wszelkiego rodzaju szalone. Dzięki, David.

Oto "/ temp.dep 3" odcinek standardowego [ISO/IEC 14882: 2003], że są one odnoszące się do:

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. [Example:

typedef double A; 
template<class T> class B { 
    typedef int A; 
}; 
template<class T> struct X : B<T> { 
    A a; // a has typedouble 
}; 

The type name A in the definition of X<T> binds to the typedef name defined in the global namespace scope, not to the typedef name defined in the base class B<T> . ] [Example:

struct A { 
    struct B { /* ... */ }; 
    int a; 
    int Y; 
}; 
int a; 
template<class T> struct Y : T { 
    struct B { /* ... */ }; 
    B b; //The B defined in Y 
    void f(int i) { a = i; } // ::a 
    Y* p; // Y<T> 
}; 
Y<A> ya; 

The members A::B , A::a , and A::Y of the template argument A do not affect the binding of names in Y<A> . ]

33

David Joyner miał historię, oto powód.

Problem przy kompilacji B<T> jest to, że jej klasa bazowa A<T> jest znana od kompilatora, będąc klasy szablon, więc nie ma mowy o kompilator wiedzieć żadnych członków z klasy bazowej.

Wcześniejsze wersje wniosły pewne wnioski, analizując bazową klasę szablonów, ale ISO C++ stwierdziło, że ten wniosek może prowadzić do konfliktów tam, gdzie nie powinno być.

Rozwiązaniem odwołać członka klasy bazowej w szablonie jest użycie this (jak ty) lub specjalnie wymienić klasę bazową:

template <typename T> class A { 
public: 
    T foo; 
}; 

template <typename T> class B: public A <T> { 
public: 
    void bar() { cout << A<T>::foo << endl; } 
}; 

Więcej informacji w gcc manual.

+8

Z jednej strony ten rodzaj ma sens. Ale z drugiej strony czuje się naprawdę kulawy. Kompilator nie * potrzebuje * wiedzieć, do czego odnosi się 'foo', dopóki nie zostanie utworzony szablon, w którym to momencie powinien być w stanie rozpoznać element' foo' w 'A'. C++ ma o wiele za dużo tych dziwacznych przypadków narożnych. –

+0

tak, to jest całkowicie nie do przyjęcia ... nie mogę wiedzieć przed utworzeniem instancji? następnie zaczekaj na wystąpienie jak zwykle w szablonach .. to jest jego duch, nie? co za bałagan ... –

8

Głównym powodem, dla którego C++ nie może zakładać niczego, jest to, że szablon podstawowy może być wyspecjalizowany dla typu później. Kontynuując przykład oryginalny:

template<> 
class A<int> {}; 

B<int> x; 
x.bar();//this will fail because there is no member foo in A<int> 
3

VC nie wdrożone odnośnika dwufazowy, a GCC robi.Zatem GCC analizuje szablony, zanim zostaną one utworzone i tym samym znajdzie więcej błędów niż VC. W twoim przykładzie foo jest nazwą zależną, ponieważ zależy od "T". Jeśli nie powiesz kompilatorowi, skąd pochodzi, nie może on w ogóle sprawdzić poprawności szablonu, zanim go utworzysz. Dlatego musisz poinformować kompilator, skąd pochodzi.

Powiązane problemy