Проблема GCC: использование члена базового класса, который зависит от аргумента шаблона - PullRequest
34 голосов
/ 14 августа 2008

Следующий код не компилируется с gcc, но работает с Visual Studio:

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

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

Я получаю ошибку:

test.cpp: в функции-члене void B :: bar () ’:

test.cpp: 11: ошибка: «foo» не был объявлен в этой области

Но так и должно быть! Если я изменю bar на

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

тогда он компилирует , но я не думаю, что должен это делать. Есть ли в официальных спецификациях C ++ что-то, что GCC следует здесь, или это просто причуды?

Ответы [ 5 ]

34 голосов
/ 14 августа 2008

У Дэвида Джойнера была история, вот причина.

Проблема при компиляции B<T> заключается в том, что его базовый класс A<T> неизвестен компилятору, являясь классом шаблона, поэтому компилятор не может узнать никаких членов из базового класса.

Более ранние версии сделали некоторый вывод, фактически проанализировав базовый класс шаблона, но ISO C ++ заявил, что этот вывод может привести к конфликтам, где их не должно быть.

Решение для ссылки на элемент базового класса в шаблоне заключается в использовании this (как вы это сделали) или, в частности, имени базового класса:

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

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

Больше информации в gcc руководстве .

19 голосов
/ 14 августа 2008

Ничего себе. C ++ не перестает удивлять меня своей странностью.

В определении шаблона неквалифицированные имена больше не будут находить членов зависимой базы (как указано в [temp.dep] / 3 в стандарте C ++). Например,

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

Вы должны сделать имена зависимыми, например, добавив им префикс this->. Вот исправленное определение C :: h,

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

В качестве альтернативного решения (к сожалению, не обратно совместимого с GCC 3.3), вы можете использовать вместо этого объявления ->:

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

Это просто сумасшествие. Спасибо, Дэвид.

Вот раздел «temp.dep / 3» стандарта [ISO / IEC 14882: 2003], к которому они относятся:

В определении шаблона класса или члена шаблона класса, если базовый класс шаблона класса зависит от параметра-шаблона, область действия базового класса не проверяется при поиске безоговорочного имени либо в точке определения шаблона класса или члена или во время создания шаблона класса или члена. [Пример:

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

Имя типа A в определении X<T> связывается с именем typedef, определенным в глобальной области имен, а не с именем typedef, определенным в базовом классе B<T>. ] [Пример:

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; 

Члены A::B, A::a и A::Y аргумента шаблона A не влияют на привязку имен в Y<A>. ]

11 голосов
/ 14 августа 2008

Это изменилось в gcc-3.4 . В этом выпуске синтаксический анализатор C ++ стал намного более строгим - в соответствии со спецификацией, но все еще немного раздражает людей с устаревшими или многоплатформенными кодами.

8 голосов
/ 15 августа 2008

Основная причина, по которой C ++ здесь не может предположить, состоит в том, что базовый шаблон может быть специализирован для типа позже. Продолжая оригинальный пример:

template<>
class A<int> {};

B<int> x; 
x.bar();//this will fail because there is no member foo in A<int>
3 голосов
/ 17 октября 2008

VC не реализует двухфазный поиск, в то время как GCC делает. Таким образом, GCC анализирует шаблоны перед их созданием и, таким образом, находит больше ошибок, чем VC. В вашем примере, foo является зависимым именем, так как оно зависит от 'T'. Если вы не укажете компилятору, откуда он взялся, он вообще не сможет проверить достоверность шаблона, прежде чем его создать. Вот почему вы должны сообщить компилятору, откуда он.

...