Domanda

Il codice seguente non viene compilato con gcc, ma lo fa con Visual Studio:

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

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

Ottengo l'errore:

prova.cpp:Nella funzione membro "void B::bar()":

prova.cpp:11:errore:"foo" non è stato dichiarato in questo ambito

Ma dovrebbe essere!Se cambio bar A

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

allora fa compilare, ma non penso di doverlo fare.C'è qualcosa nelle specifiche ufficiali del C++ che GCC sta seguendo qui o è solo una stranezza?

È stato utile?

Soluzione

Questo è cambiato gcc-3.4.Il parser C++ è diventato molto più rigoroso in quella versione, secondo le specifiche ma ancora piuttosto fastidioso per le persone con codebase legacy o multipiattaforma.

Altri suggerimenti

David Joyner aveva la storia, ecco il motivo.

Il problema durante la compilazione B<T> è che la sua classe base A<T> è sconosciuto al compilatore, essendo una classe modello, quindi non è possibile per il compilatore conoscere alcun membro della classe base.

Le versioni precedenti eseguivano alcune inferenze analizzando effettivamente la classe del modello di base, ma ISO C++ affermava che questa inferenza può portare a conflitti dove non dovrebbero esserci.

La soluzione per fare riferimento a un membro della classe base in un modello è utilizzare this (come hai fatto tu) o nominare specificamente la classe base:

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

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

Maggiori informazioni in manuale del gcc.

Oh.Il C++ non smette mai di sorprendermi con le sue stranezze.

In una definizione di modello, i nomi non qualificati non troveranno più i membri di una base dipendente (come specificato da [temp.dep]/3 nello standard C++).Per esempio,

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
  }
};

È necessario rendere dipendenti i nomi, ad es.prefissandoli con questo->.Ecco la definizione corretta di C::h,

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

Come soluzione alternativa (sfortunatamente non retrocompatibile con GCC 3.3), puoi utilizzare le dichiarazioni using invece di questa->:

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 ();
  }
};

E' proprio una follia.Grazie, Davide.

Ecco la sezione "temp.dep/3" dello standard [ISO/IEC 14882:2003] a cui fanno riferimento:

Nella definizione di un modello di classe o di un membro di un modello di classe, se una classe base del modello di classe dipende da un parametro di modello, l'ambito della classe base non viene esaminato durante la ricerca del nome non qualificato né nel punto di definizione della classe modello o membro o durante un'istanza del modello o membro della classe.[Esempio:

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

Il nome del tipo A nella definizione di X<T> si lega al nome typedef definito nell'ambito dello spazio dei nomi globale, non al nome typedef definito nella classe base B<T>.] [Esempio:

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; 

I membri A::B, A::a, E A::Y dell'argomento modello A non influiscono sull'associazione dei nomi in Y<A>. ]

Il motivo principale per cui il C++ non può assumere nulla in questo caso è che il modello di base può essere specializzato per un tipo successivo.Continuando l'esempio originale:

template<>
class A<int> {};

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

VC non implementa la ricerca in due fasi, mentre GCC lo fa.Pertanto GCC analizza i modelli prima che vengano istanziati e quindi trova più errori di VC.Nel tuo esempio, foo è un nome dipendente, poiché dipende da "T".A meno che non si dica al compilatore da dove proviene, non sarà possibile verificare la validità del modello prima di crearne un'istanza.Ecco perché devi dire al compilatore da dove viene.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top