Pergunta

O código a seguir não é compilado com o gcc, mas sim com o Visual Studio:

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

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

Eu recebo o erro:

teste.cpp:Na função membro ‘void B::bar()’:

teste.cpp:11:erro:'foo' não foi declarado neste escopo

Mas deveria ser!Se eu mudar bar para

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

então isso faz compilar, mas acho que não preciso fazer isso.Há algo nas especificações oficiais do C++ que o GCC está seguindo aqui ou é apenas uma peculiaridade?

Foi útil?

Solução

Isso mudou em gcc-3.4.O analisador C++ ficou muito mais rigoroso nessa versão - de acordo com as especificações, mas ainda um pouco irritante para pessoas com bases de código legadas ou multiplataforma.

Outras dicas

David Joyner tinha a história, aqui está o motivo.

O problema ao compilar B<T> é que sua classe base A<T> é desconhecido do compilador, sendo uma classe de modelo, portanto, não há como o compilador conhecer quaisquer membros da classe base.

Versões anteriores fizeram algumas inferências analisando a classe do modelo base, mas ISO C++ afirmou que essa inferência pode levar a conflitos onde não deveriam haver.

A solução para referenciar um membro da classe base em um modelo é usar this (como você fez) ou nomeie especificamente a 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; }
};

Mais informações em manual do gcc.

Uau.C++ nunca para de me surpreender com sua estranheza.

Em uma definição de modelo, nomes não qualificados não encontrarão mais membros de uma base dependente (conforme especificado por [temp.dep]/3 no padrão C++).Por exemplo,

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

Você deve tornar os nomes dependentes, por ex.prefixando-os com isto->.Aqui está a definição corrigida de C::h,

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

Como uma solução alternativa (infelizmente não compatível com versões anteriores do GCC 3.3), você pode usar declarações em vez disso->:

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

Isso é todo tipo de loucura.Obrigado, Davi.

Aqui está a seção "temp.dep/3" da norma [ISO/IEC 14882:2003] à qual eles se referem:

Na definição de um modelo de classe ou de um membro de um modelo de classe, se uma classe base do modelo de classe depende de um parâmetro de modelo, o escopo da classe base não é examinado durante a pesquisa de nome não qualificado no ponto de definição da classe modelo ou membro ou durante uma instanciação do modelo ou membro de classe.[Exemplo:

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

O nome do tipo A na definição de X<T> liga-se ao nome typedef definido no escopo do namespace global, não ao nome typedef definido na classe base B<T>.] [Exemplo:

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; 

Os membros A::B, A::a, e A::Y do argumento do modelo A não afetam a vinculação de nomes em Y<A>. ]

A principal razão pela qual o C++ não pode assumir nada aqui é que o modelo base pode ser especializado para um tipo posteriormente.Continuando o exemplo original:

template<>
class A<int> {};

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

O VC não implementa pesquisa em duas fases, enquanto o GCC o faz.Portanto, o GCC analisa os modelos antes de serem instanciados e, portanto, encontra mais erros que o VC.No seu exemplo, foo é um nome dependente, pois depende de 'T'.A menos que você diga ao compilador de onde ele vem, ele não poderá verificar a validade do modelo antes de instanciá-lo.É por isso que você precisa informar ao compilador de onde vem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top