2009-03-03 2 views
26

Rozważmy następujący kod:Dostęp do zmiennej odziedziczoną matrycy klasy nadrzędnej

template<class T> class Foo 
{ 
public: 
    Foo() { a = 1; } 

protected: 
    int a; 
}; 

template<class T> class Bar : public Foo<T> 
{ 
public: 
    Bar() { b = 4; }; 

    int Perna(int u); 

protected: 
    int b; 

}; 

template<class T> int Bar<T>::Perna(int u) 
{ 
    int c = Foo<T>::a * 4; // This works 
    return (a + b) * u; // This doesn't 
} 

g ++ 3.4.6, 4.3.2 i 4.1.2 dają error

test.cpp: In member function `int Bar<T>::Perna(int)': 
test.cpp:25: error: `a' was not declared in this scope 

g ++ 2.96 i MSVC 6, 7 , 7.1, 8 i 9 akceptują to, podobnie jak (przynajmniej) starsze kompilatory Intel i SGI C++.

Czy nowy kompilator Gnu C++ jest zgodny ze standardem, czy nie? Jeśli tak, jakie są powody, dla których ta dziedzicząca klasa nie jest w stanie zobaczyć chronionej odziedziczonej zmiennej członkowskiej?

Ponadto, jeśli istnieje

int A() { return a; } 

w Foo, ja się błąd

test.cpp:25: error: there are no arguments to A that depend on a template parameter, so a declaration of A must be available 
test.cpp:25: error: (if you use -fpermissiveâ, G++ will accept your code, but allowing the use of an undeclared name is deprecated) 

gdy próbuję go użyć w funkcji składowej barskiej. Ciekawe jest również: Bar dziedziczy Foo, więc myślę, że jest oczywiste, że A() w zakresie Bar to Foo :: A().

Odpowiedz

37

Późniejsze wersje GCC prawidłowo implementują standard.

Standard określa, że ​​niewykwalifikowane nazwy w szablonie są niezależne i muszą zostać sprawdzone po zdefiniowaniu szablonu. Definicja zależnej klasy bazowej jest w tym czasie nieznana (mogą istnieć specjalizacje szablonu klasy podstawowej), więc nie można rozstrzygnąć niewykwalifikowanych nazw.

Dotyczy to zarówno nazw zmiennych, jak i funkcji zadeklarowanych w klasie bazowej.

Jak zauważyłeś, rozwiązaniem jest podanie kwalifikowanej nazwy zmiennej lub funkcji lub podanie deklaracji "używanie". Na przykład.

template<class T> 
int Bar<T>::Perna(int u) 
{ 
    int c = Foo<T>::a * 4; // This works 
    c = this->a * 4; // and this 

    using Foo<T>::a; 
    c = a * 4; // and with 'using', so should this 
} 

(Jestem faktycznie nie jest w 100% pewny o poprawnej składni dla wersji z wykorzystaniem i nie można sprawdzić tutaj, ale masz pomysł).

+0

Dzięki. Zamierzam przetoczyć rękawy i zmienić kod. –

+1

Wierzę, że musisz zrobić instrukcję użycia na poziomie klasy, to jest szablon Bar: publiczny Foo {używając Foo :: a; ...}; –

4

Komunikat o błędzie wyświetlany przez GCC pokazuje, że wersja GCC nadal zawiera błąd rozwiązany tylko w wersjach trunkingowych GCC4.7. Starsze wersje, w tym GCC4.1 chętnie przyjąć następujący kod

template<typename T> 
struct A { 
    void f(int) { } 
}; 

template<typename T> 
struct B : A<T> { 
    void g() { T t = 0; f(t); } 
}; 

int main() { 
    B<int> b; b.g(); 
} 

GCC będzie patrzeć f w f(t) w klasie bazowej A<T> i znajdzie deklarację w klasie bazowej. GCC robi to, ponieważ f jest zależny od, ponieważ istnieją argumenty dla f, które "zależą od parametru szablonu" (spójrz na komunikat o błędzie, który ci dał!). Ale standard zabrania GCC to zrobić z dwóch powodów

  1. Standard mówi, że korzystanie z niekwalifikowanych nazw nigdy nie znajdzie deklarację w sposób zależny od klasy bazowej bez względu na to, czy nazwa jest zależna.

  2. Standard mówi, że zależne wyszukiwanie nazwy funkcji w czasie tworzenia wystąpi tylko w ADL.

GCC 4.7 prawidłowo implementuje standard w tym zakresie.