Следующий код не компилируется с помощью 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: In member function 'void B
::bar()':
test.cpp:11: error: 'foo' was not declared in this scope
Но оно должно быть объявлено! Если я изменю bar
на
void bar() { cout << this->foo << endl; }
тогда он достаточно компилируется, но я не думаю, что я должен это делать. Есть ли что-то в официальных спецификациях C++, чему GCC следует здесь, или это просто причуда?
У Дэвида Джойнера была история, вот причина.
Проблема при компиляции 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 manual.
Ух ты. 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), вы можете использовать декларации вместо this->:
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>
. ]
Это изменилось в gcc-3.4. В этом выпуске парсер C++ стал намного строже - в соответствии со спецификацией, но все еще немного раздражает людей с устаревшими или многоплатформенными базами кода.
Основная причина C++ не может ничего предполагать здесь заключается в том, что базовый шаблон может быть специализированным для определенного типа позже. Продолжая исходный пример:
template<>
class A<int> {};
B<int> x;
x.bar();//this will fail because there is no member foo in A<int>
ВК Не'т реализованы два этапа подстановки, в то время как gcc выполняет. Так ССЗ анализирует шаблоны, прежде чем они создаются и, следовательно, находит больше ошибок, чем ВК. В вашем примере, Foo является зависимым именем, так как это зависит от 'Т'. Если вы говорите компилятору, откуда она берется, он не может проверить валидность шаблона на все, прежде чем вы инстанцировать его. Что's, почему вы должны сказать компилятору, где он приходит от.