Pourquoi pas une classe de modèle dérivé ont accès aux identifiants d'une classe de modèle de base?

StackOverflow https://stackoverflow.com/questions/1239908

Question

Considérez:

template <typename T>
class Base
{
    public:
        static const bool ZEROFILL = true;
        static const bool NO_ZEROFILL = false;
}

template <typename T>
class Derived : public Base<T>
{
    public: 
        Derived( bool initZero = NO_ZEROFILL );    // NO_ZEROFILL is not visible
        ~Derived();
}

Je ne suis pas en mesure de la compilation avec GCC ce g ++ 3.4.4 (Cygwin).

Avant de convertir ces modèles de classe, ils étaient non-générique et la classe dérivée a été en mesure de voir les membres statiques de la classe de base. Est-ce la perte de visibilité dans une exigence de la spécification C ++ ou est-il un changement de syntaxe que je dois employer?

Je comprends que chaque instanciation de Base<T> aura son propre membre statique « ZEROFILL » et « NO_ZEROFILL », que Base<float>::ZEROFILL et Base<double>::ZEROFILL sont différentes variables, mais je ne se soucient vraiment pas; la est là constante pour une meilleure lisibilité du code. Je voulais utiliser une constante statique, car cela est plus sûr en termes de conflits de noms plutôt que d'une macro ou globale.

Était-ce utile?

La solution

C'est recherche en deux phases pour vous.

Base<T>::NO_ZEROFILL (tous les identifiants de bouchons sont boo, à l'exception des macros, BTW) est un identifiant qui dépend de T.
Depuis, lorsque le compilateur d'abord le modèle parse, il n'y a pas de type réel encore substitué à T, le compilateur ne « sait » ce que Base<T> est. Donc, il ne peut pas connaître les identifiants vous assumez à définir en elle (il pourrait y avoir une spécialisation pour certains Ts que le compilateur ne voit que plus tard) et vous ne pouvez pas omettre la qualification de classe de base à partir des identifiants définis dans la classe de base.

C'est pourquoi vous devez écrire Base<T>::NO_ZEROFILL (ou this->NO_ZEROFILL). Cela indique au compilateur que NO_ZEROFILL est quelque chose dans la classe de base, qui dépend de T, et qu'il ne peut le vérifier plus tard, lorsque le modèle est instancié. Il sera donc l'accepter sans essayer de vérifier le code.
Ce code ne peut être vérifiée plus tard, lorsque le modèle est instancié en fournissant un paramètre réel pour T.

Autres conseils

Le problème que vous avez rencontré est dû au nom des règles de consultation pour les classes de base dépendantes. 14,6 / 8 a:

  

Lors de la recherche de la déclaration d'un nom utilisé dans une définition de modèle, les règles de consultation habituelle (3.4.1,   3.4.2) sont utilisés pour les noms non dépendantes. La recherche des noms en fonction des paramètres du modèle est   reporté jusqu'à ce que l'argument de modèle réel est connu (14.6.2).

(Ce n'est pas vraiment « recherche 2 phases. » - voir ci-dessous pour une explication de cela)

Le point sur les 14,6 / 8 est que dans la mesure où le compilateur est concerné NO_ZEROFILL dans votre exemple est un identifiant et ne dépend pas du paramètre de modèle. Il est donc recherché selon les règles normales en 3.4.1 et 3.4.2.

Cette recherche normale ne cherche pas à l'intérieur Base<T> et ainsi NO_ZEROFILL est tout simplement un identifiant non déclaré. 14.6.2 / 3 a:

  

Dans la définition d'un modèle de classe ou un membre d'un modèle de classe, si une classe de base du modèle de classe   dépend d'un modèle-paramètre, la portée de la classe de base ne sont pas examinés au cours de nom non qualifié recherche   soit au moment de la définition du modèle de classe ou d'un membre ou lors d'une instanciation du modèle de classe   ou un membre.

Lorsque vous êtes admissible NO_ZEROFILL avec Base<T>:: en substance, vous changez d'être un nom non dépendant d'une personne à charge et quand vous faites cela, vous retardez sa recherche jusqu'à ce que le modèle est instancié.

Side note: Qu'est-ce que recherche 2 phases:

void bar (int);

template <typename T>
void foo (T const & t) {
  bar (t);
}


namespace NS
{
  struct A {};
  void bar (A const &);
}


int main ()
{
  NS::A a;
  foo (a);
}

L'exemple ci-dessus est compilé comme suit. Le compilateur analyse le corps de la fonction de foo et de voir qu'il y a un appel à bar qui présente un argument dépendante (ie. Celle qui dépend du paramètre de modèle). A ce stade, le compilateur regarde bar par 3.4.1 et c'est la « phase 1 recherche ». La recherche se trouve la fonction void bar (int) et qui est stocké à l'appel dépend à plus tard.

Lorsque le modèle est ensuite instancié (par suite de l'appel de main), le compilateur effectue ensuite une recherche supplémentaire dans le champ d'application de l'argument, ceci est la « recherche de la phase 2 ». Cette affaire qui aboutit à trouver void NS::bar(A const &).

Le compilateur a deux pour bar et surcharges il sélectionne entre eux, dans le cas ci-dessus appelant void NS::bar(A const &).

Semble compiler ok dans vs 2008. Avez-vous essayé:

public:
    Derived( bool initZero = Base<T>::NO_ZEROFILL );

Essayez ce programme

#include<iostream>
using namespace std;
template <class T> class base{
public:
T x;
base(T a){x=a;}
virtual T get(void){return x;}
};
template <class T>
class derived:public base<T>{
public:
derived(T a):base<T>(a){}
T get(void){return this->x+2;}
};
int main(void){
base<int> ob1(10);
cout<<ob1.get()<<endl;
derived<float> ob(10);
cout<<ob.get();
return 0;
}

dans la ligne de T get(void){return this->x+2;} u peut également utiliser la résolution de portée (: :) opérateur. par exemple, essayez de remplacer la ligne avec

T get(void){return base<T>::x+2;}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top