Question

Le code suivant ne se compile pas avec gcc, mais avec Visual Studio :

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

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

J'obtiens l'erreur :

test.cpp :Dans la fonction membre 'void B::bar()' :

test.cpp:11 :erreur:'foo' n'a pas été déclaré dans ce champ d'application

Mais ça devrait l’être !Si je change bar à

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

puis il fait compiler, mais je ne pense pas devoir le faire.Y a-t-il quelque chose dans les spécifications officielles de C++ que GCC suit ici, ou est-ce juste une bizarrerie ?

Était-ce utile?

La solution

Cela a changé dans gcc-3.4.L'analyseur C++ est devenu beaucoup plus strict dans cette version - conformément aux spécifications, mais toujours un peu ennuyeux pour les personnes disposant de bases de code héritées ou multiplateformes.

Autres conseils

David Joyner avait l'histoire, voici la raison.

Le problème lors de la compilation B<T> est-ce que c'est sa classe de base A<T> est inconnu du compilateur, étant une classe modèle, donc aucun moyen pour le compilateur de connaître les membres de la classe de base.

Les versions antérieures faisaient des inférences en analysant la classe de modèle de base, mais ISO C++ a déclaré que cette inférence peut conduire à des conflits là où il ne devrait pas y en avoir.

La solution pour référencer un membre de la classe de base dans un modèle consiste à utiliser this (comme vous l'avez fait) ou nommez spécifiquement la classe de 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; }
};

Plus d'informations dans manuel gcc.

Ouah.Le C++ ne cesse de me surprendre par son étrangeté.

Dans une définition de modèle, les noms non qualifiés ne trouveront plus les membres d'une base dépendante (comme spécifié par [temp.dep]/3 dans la norme C++).Par exemple,

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

Vous devez rendre les noms dépendants, par ex.en les préfixant avec this->.Voici la définition corrigée de C::h,

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

Comme solution alternative (malheureusement non compatible avec GCC 3.3), vous pouvez utiliser des déclarations using au lieu de ceci-> :

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

C'est tout simplement fou.Merci, David.

Voici la section "temp.dep/3" de la norme [ISO/IEC 14882:2003] à laquelle ils font référence :

Dans la définition d'un modèle de classe ou d'un membre d'un modèle de classe, si une classe de base du modèle de classe dépend d'un paramètre de modèle, la portée de la classe de base n'est pas examinée lors de la recherche de nom non qualifié ni au moment de la définition de la classe. modèle ou membre ou lors d’une instanciation du modèle ou du membre de classe.[Exemple:

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

Le nom du type A dans la définition de X<T> se lie au nom de typedef défini dans la portée de l'espace de noms global, et non au nom de typedef défini dans la classe de base B<T>.] [Exemple:

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; 

Les membres A::B, A::a, et A::Y de l'argument du modèle A n'affecte pas la liaison des noms dans Y<A>. ]

La principale raison pour laquelle C++ ne peut rien supposer ici est que le modèle de base peut être spécialisé pour un type ultérieurement.Pour continuer l'exemple original :

template<>
class A<int> {};

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

VC n'implémente pas de recherche en deux phases, contrairement à GCC.Ainsi, GCC analyse les modèles avant qu'ils ne soient instanciés et trouve ainsi plus d'erreurs que VC.Dans votre exemple, foo est un nom dépendant, puisqu'il dépend de « T ».À moins que vous n'indiquiez au compilateur d'où il vient, il ne peut pas du tout vérifier la validité du modèle avant de l'instancier.C'est pourquoi vous devez indiquer au compilateur d'où il vient.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top