2009-03-03 15 views
26

Si consideri il seguente codice:Accesso variabile ereditata dalla classe genitore template

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 e 4.1.2 dare errore

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

g ++ 2.96 e MSVC 6, 7 , 7.1, 8 e 9 lo accettano, così come (almeno) i vecchi compilatori C++ Intel e SGI.

Il nuovo compilatore Gnu C++ rispetta lo standard o no? Se lo fanno, qual è la logica alla base di questa classe ereditaria non è in grado di vedere una variabile membro ereditata protetta?

Inoltre, se c'è

int A() { return a; } 

a Foo, ottengo l'errore

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) 

quando cerco di usarlo in una funzione membro di Bar. Trovo altrettanto curioso: Bar eredita Foo, quindi penso che sia ovvio che A() nell'ambito di Bar sia Foo :: A().

risposta

37

Le versioni successive di GCC implementano correttamente lo standard.

Lo standard specifica che i nomi non qualificati in un modello sono non dipendenti e devono essere cercati quando il modello è definito. La definizione di una classe di base dipendente è sconosciuta in quel momento (possono esistere specializzazioni del modello di classe base), pertanto i nomi non qualificati non possono essere risolti.

Ciò vale sia per i nomi di variabili sia per quelli di funzione dichiarati nella classe base.

Come è stato osservato, la soluzione è fornire il nome qualificato della variabile o della funzione o fornire una dichiarazione di "utilizzo". Per esempio.

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 
} 

(In realtà sono al 100% sicuro circa la sintassi corretta per la versione con e non riesco a provare da qui, ma si ottiene l'idea).

+0

Grazie. Ho intenzione di rimboccarmi le maniche e cambiare il codice. –

+1

Credo che sia necessario eseguire l'istruzione using a livello di classe, ad esempio il modello Bar: public Foo {using Foo :: a; ...}; –

4

Il messaggio di errore restituito da GCC indica che la versione di GCC conteneva ancora un bug risolto solo nelle versioni trunk di GCC4.7. Le versioni precedenti, tra cui GCC4.1 sarà lieto di accettare seguente codice

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 cercherà f nel f(t) all'interno della classe base A<T> e troveranno la dichiarazione nella classe base. GCC lo fa perché f dipende dallo, perché ci sono argomenti a f che "dipendono da un parametro di modello" (guarda il suo messaggio di errore che ti ha dato!). Ma la norma vieta GCC di farlo per due ragioni

  1. La norma dice che l'uso di nomi non qualificati non potrà mai trovare una dichiarazione in una classe base dipendono non importa se il nome è dipendente.

  2. Lo standard dice che la ricerca dipendente di un nome di funzione al momento dell'istanziazione farà solo ADL.

GCC 4.7 implementa correttamente lo standard a tale riguardo.

Problemi correlati