Perché una classe template derivata non ha accesso ad una base di identificatori modello di classe?

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

Domanda

Si consideri:

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

Non sono in grado di compilazione con GCC questo g ++ 3.4.4 (Cygwin).

Prima di convertire questi per i modelli di classe, erano non generico e la classe derivata è stato in grado di vedere i membri statici della classe base. È questa perdita di visibilità in un requisito della specifica C ++ o c'è un cambiamento di sintassi che ho bisogno di impiegare?

I capire che ogni istanza di Base<T> avrà un proprio membro statico "ZEROFILL" e "NO_ZEROFILL", che Base<float>::ZEROFILL e Base<double>::ZEROFILL sono variabili diverse, ma io non mi interessa; la costante è lì per migliorare la leggibilità del codice. Volevo usare una costante statica, perché è più sicuro in termini di conflitti di nome, piuttosto che una macro o globale.

È stato utile?

Soluzione

Questo è il lookup a due fasi per voi.

Base<T>::NO_ZEROFILL (tutti i tappi identificatori sono boo, tranne che per le macro, BTW) è un identificatore che dipende T.
Dal momento che, quando il compilatore analizza prima il modello, non c'è alcun tipo effettivo sostituito T ancora, il compilatore non "sa" cosa è Base<T>. Quindi non può conoscere eventuali identificatori utente si assume essere definito in esso (ci potrebbe essere una specializzazione per alcuni Ts che il compilatore vede solo più tardi) e non è possibile omettere la qualifica classe base da identificatori definiti nella classe base.

Ecco perché si deve scrivere Base<T>::NO_ZEROFILL (o this->NO_ZEROFILL). Questo dice al compilatore che NO_ZEROFILL è qualcosa nella classe base, che dipende T, e che si può solo verificare in un secondo momento, quando il modello viene creata un'istanza. Sarà quindi accettarla senza cercare di verificare il codice.
Questo codice può essere verificata solo più tardi, quando il modello è un'istanza fornendo un parametro attuale per T.

Altri suggerimenti

Il problema riscontrato è dovuto alle regole nome di ricerca per le classi di base dipendenti. 14,6 / 8 ha:

  

Quando si cerca per la dichiarazione di un nome usato in una definizione di modello, le regole di consueto di ricerca (3.4.1,   3.4.2) sono utilizzati per i nomi nondependent. La ricerca di nomi dipendente dai parametri del modello è   rinviata è noto l'argomento vero e proprio modello (14.6.2).

(Questo non è proprio "lookup 2 fasi." - vedi sotto per una spiegazione di che)

Il punto circa 14,6 / 8 è che per quanto riguarda il compilatore è interessato NO_ZEROFILL nel tuo esempio è un identificatore e non dipende dal parametro di template. E 'quindi alzò lo sguardo come per le normali regole in 3.4.1 e 3.4.2.

Questa ricerca normale non cercare dentro Base<T> e così NO_ZEROFILL è semplicemente un identificatore non dichiarato. 14.6.2 / 3 ha:

  

Nella definizione di un modello di classe o un membro di un modello di classe, se una classe di base del modello di classe   dipende da un modello-parametro, la portata classe base non viene esaminato durante ricerca nome qualificato   sia al momento della definizione del modello di classe o di un membro o nel corso di un'esemplificazione del modello di classe   o membro.

Quando si qualificano NO_ZEROFILL con Base<T>:: in sostanza si sta cambiando da essere un nome non dipende in una dipendente e quando lo fai si ritarda la sua ricerca fino a quando il modello viene creata un'istanza.

Nota a margine: Che cosa è ricerca 2 fasi:

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'esempio precedente è compilato come segue. Il compilatore analizza il corpo della funzione di foo e vedere che c'è una chiamata a bar che ha un argomento dipendente (es. Uno che dipende dal parametro di template). A questo punto il compilatore cerca up bar di cui al 3.4.1 e questa è la "fase 1 lookup". La ricerca troverà la funzione void bar (int) e che viene memorizzato con la chiamata dipendente a più tardi.

Quando il modello viene poi istanziata (in conseguenza della chiamata da main), il compilatore esegue quindi una ricerca aggiuntiva nell'ambito dell'argomento, questa è la "fase 2 ricerca". Questo caso che si traduce nella ricerca di void NS::bar(A const &).

Il compilatore ha due overload per bar e seleziona tra loro, nel caso di cui sopra chiamando void NS::bar(A const &).

Sembra di compilare ok in vs 2008. Hai provato:

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

Prova questo programma

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

nella linea T get(void){return this->x+2;} u può utilizzare anche la risoluzione di ambito (: :) operatore. per esempio, provare a sostituire la linea con il

T get(void){return base<T>::x+2;}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top