Problema GCC: folosirea unui membru al unei clase de bază care depinde de un argument de șablon

Următorul cod nu se compilează cu gcc, dar se întâmplă cu Visual Studio:

template  class A {
public:
    T foo;
};

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

Eu primesc eroarea:

test.cpp: În funcția membrului? void B :: bar ()?:

     

test.cpp: 11: eroare:? foo? nu a fost declarată în acest scop

Dar ar trebui să fie! Dacă schimbați bar la

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

atunci nu compilează, dar nu cred că trebuie să fac asta. Există ceva în specificațiile oficiale ale C ++ că CCG urmărește aici sau este doar o întrebare?

0
fr hi bn
Acest lucru se întâmplă din cauza căutării în două faze a numelui (pe care nu o folosesc toți compilații implicit). Există 4 soluții la această problemă: 1) Utilizați prefixul A :: foo , , 4) Utilizați un compilator global comutator care permite modul permisiv. Pro și contra acestor soluții sunt descrise în adăugat autor KarolaN, sursa

5 răspunsuri

David Joyner avea istoria, aici este motivul.

The problem when compiling B is that its base class A is unknown from the compiler, being a template class, so no way for the compiler to know any members from the base class.

Versiunile anterioare au făcut o anumită inferență, de fapt parsând clasa de șabloane de bază, dar ISO C ++ a declarat că această inferență poate duce la conflicte acolo unde nu ar trebui să fie.

Soluția de referință a unui membru de clasă de bază într-un șablon este să utilizați acest (ca și dvs.) sau să denumiți în mod specific clasa de bază:

template  class A {
public:
    T foo;
};

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

Mai multe informații în manual gcc .

0
adăugat
Pe de o parte, acest tip are sens. Dar, pe de altă parte, se simte într-adevăr lame. Compilatorul nu are nevoie să știe ce foo se referă până când șablonul este instanțiat, moment în care ar trebui să recunoască codul foo membru în A . C ++ are prea multe dintre aceste cazuri ciudate.
adăugat autor Derek Park, sursa
da, acest lucru este total inacceptabil ... cant știți înainte de instanțiere? apoi așteptați pentru instanțiere ca de obicei în șabloane .. asta e spiritul, nu? ce mizerie...
adăugat autor neu-rah, sursa

Principalul motiv pentru care C ++ nu poate presupune nimic aici este faptul că șablonul de bază poate fi specializat pentru un tip mai târziu. Continuând exemplul original:

template<>
class A {};

B x; 
x.bar();//this will fail because there is no member foo in A
0
adăugat

Acest lucru sa schimbat în gcc-3.4 . Parserul C ++ a devenit mult mai strict în această versiune - pe spec. Dar totuși destul de enervant pentru persoanele cu baze de coduri vechi sau multi-platformă.

0
adăugat

Wow. C ++ nu încetează niciodată să mă surprindă cu ciudățenia ei.

Într-o definiție de șablon, numele necalificate nu vor mai găsi membrii unei baze dependente (așa cum este specificat de [temp.dep]/3 în standardul C ++). De exemplu,

template  struct B {
  int m;
  int n;
  int f ();
  int g ();
};
int n;
int g ();
template  struct C : B {
  void h ()
  {
    m = 0;//error
    f (); //error
    n = 0;//::n is modified
    g (); //::g is called
  }
};

You must make the names dependent, e.g. by prefixing them with this->. Here is the corrected definition of C::h,

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

As an alternative solution (unfortunately not backwards compatible with GCC 3.3), you may use using declarations instead of this->:

template  struct C : B {
  using B::m;
  using B::f;
  using B::n;
  using B::g;
  void h ()
  {
    m = 0;
    f ();
    n = 0;
    g ();
  }
};

Asta e tot felul de nebuni. Mulțumesc, David.

Iată secțiunea "temp.dep/3" din standardul [ISO/IEC 14882: 2003] la care se referă:

În definiția unui șablon de clasă sau a unui membru al unui șablon de clasă, dacă o clasă de bază a șablonului de clasă depinde de un parametru de șablon, domeniul de bază al clasei de bază nu este examinat în timpul căutării necalificate a numelui, fie în punctul de definiție a șablonului de clasă sau a unui membru sau în timpul unei instanții a șablonului de clasă sau a unui membru. [Exemplu:

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

The type name A in the definition of X binds to the typedef name defined in the global namespace scope, not to the typedef name defined in the base class B. ] [Example:

struct A { 
    struct B { /* ... */ }; 
    int a; 
    int Y; 
}; 
int a; 
template struct Y : T { 
    struct B { /* ... */ }; 
    B b; //The B defined in Y 
    void f(int i) { a = i; }//::a 
    Y* p;//Y 
}; 
Y ya; 

The members A::B, A::a, and A::Y of the template argument A do not affect the binding of names in Y. ]

VC nu efectuează o căutare în două faze, în timp ce GCC face. Deci GCC analizează șabloane înainte de a fi instanțiate și astfel găsește mai multe erori decât VC. În exemplul dvs., foo este un nume dependent, deoarece depinde de "T". Dacă nu îi spuneți compilatorului de unde provine, nu poate verifica deloc valabilitatea șablonului, înainte de ao instanțializa. De aceea trebuie să-i spui compilatorului de unde provine.

0
adăugat