Проблема ССАГПЗ:использование члена базового класса, который зависит от аргумента шаблона

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

Вопрос

Следующий код не компилируется с помощью 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()’:

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

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

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

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

Это было полезно?

Решение

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

Другие советы

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

Проблема при компиляции 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.

Ух ты.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) вы можете использовать объявления using вместо этого->:

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>. ]

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

template<>
class A<int> {};

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top