template<class T> base_class
{
public:
int base_var;
};
template<class T> derived_class : public base_class<T>
{
public:
int f()
{
return base_var;
}
};
И он не видит base_var. Видит только this->base_var
или (что лучше) base_class<T>::base_var.
Первый вопрос — почему так?
Второй вопрос — если так, то почему резолвится this->base_var? Ведь this это не base_class<T>, это — тот же самый derived_class<T>?
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>Делаем так: MS>
MS>template<class T> base_class
MS>{
MS>public:
MS> int base_var;
MS>};
MS>template<class T> derived_class : public base_class<T>
MS>{
MS>public:
MS> int f()
MS> {
MS> return base_var;
MS> }
MS>};
MS>
MS>И он не видит base_var. Видит только this->base_var MS>или (что лучше) base_class<T>::base_var.
MS>Первый вопрос — почему так?
По стандарту. И не только gcc, но и VC++ 7.1 делает то же самое с опцией /Za.
Этот вопрос, по-моему, уже обсуждался в форуме. Можно глянуть Вандервуда и Джоссатиса C++ templates -- там это точно было.
Если коротко -- проблема в том, когда именно производить разрешение имен. В точке определения или в точке "воплощения" шаблона. В точке определения компилятор не может знать ничего о шаблонном базовом классе -- из-за специализации. Имя base_var не является dependent name в данном случае, поэтому разрешается(с ошибкой) в точке определения шаблона.
this -> base_var
base_class<T>::base_var -- dependent name (грубо говоря, имеет "существенную" зависимость от параметров шаблона).
MS>Второй вопрос — если так, то почему резолвится this->base_var? Ведь this это не base_class<T>, это — тот же самый derived_class<T>?
this -> derived_class<T>::base_var -- вот так может быть более ясно
> И он не видит base_var. Видит только this->base_var > или (что лучше) base_class<T>::base_var.
Ради фана добавь перед определением derived_class<> глобальный int base_var; *
> Первый вопрос — почему так?
base_var — это argument dependant name. Так как компилятору об этом неизвестно (ты явно scope не указал через this-> или base_class<T>:), то он предполагает, что это обычное имя и осуществляет привязку в точке определения шаблона и просматривает derived_class<> scope, затем namespace scope, затем global scope.
> Второй вопрос — если так, то почему резолвится this->base_var? Ведь this это не base_class<T>, это — тот же самый derived_class<T>?
Потому что ты явно указываешь компиятору scope этого имени и указываешь что нужно искать в scope этого класса и базовых классов, что откладывает поиск имени до момента инстанцирования.
* Именно поэтому в шаблонных классах удобно все члены (и дата, и ф-ции) префиксить this-> — это спасает от неожиданностей.
Здравствуйте, McSeem2, Вы писали:
MS>Делаем так: MS>
MS>template<class T> base_class
MS>{
MS>public:
MS> int base_var;
MS>};
MS>template<class T> derived_class : public base_class<T>
MS>{
MS>public:
MS> int f()
MS> {
MS> return base_var;
MS> }
MS>};
MS>
MS>И он не видит base_var. Видит только this->base_var MS>или (что лучше) base_class<T>::base_var.
MS>Первый вопрос — почему так?
Во-первых, стоит упомянтуть, что просто 'base_var' — это так называемое неквалифицированное имя, 'base_class<T>::base_var' — это квалифицированное имя, а 'this->base_var' — это доступ к члену класса. Разрешение имен в этих трех случаях управляется тремя различными разделами стандарта языка — '3.4.1 Unquialified name lookup', '3.4.3 Qualified name lookup' и '3.4.5 Class member access' соответственно. Неудивительно, что правила разрешения имен в каждом случае отличаются.
Во-вторых, даже более важно в данном случае то, в процессе разрешения имен существует ряд специфичных для шаблонов особенностей, описанные внутри разделов 14.6.xxx. Одним из ключевых моментов при этом является то, классифицируется ли контекст (в данном случае — выражение), в котором использовано данное имя, зависимым (dependent) или независимым (non-dependent) от параметров шаблона. Разрешение имен в зависимых контекстах откладывается до момента инстанциирования, когда конкретные аргументы шаблона уже известны. Разрешение имен в независимых контекстах делается сразу.
В твоем примере выражение 'this->base_var' является зависимым, потому что его подвыражение 'this' является зависимым (см. 14.6.2.2/1). Подвыражение 'this', в свою очередь, является зависимым, потому что тип 'derived_class' является зависимым (см. 14.6.2.2/2). Тип 'derived_class' является зависимым, потому что он включает зависимый тип 'base_class<T>' (см. 14.6.2.1/1 и 3.9.2/1). Поэтому разрешение имени 'base_var' откладывается до момента инстанциирования.
Выражение 'base_class<T>::base_var' является зависимым по аналогичным причинам.
А вот выражение 'base_var' зависимым не является (оно просто не подходит ни под один пункт определения зависисмого выражения) поэтому имя 'base_var' в данном случае будет разрешаться сразу, по правилам 3.4.1, но с учетом поправки 14.6.2/3, где сказано, что зависящий от параметра шаблона базовый класс не рассматривается в процессе разрешения имен. Т.е. в твоем примере при разрешении простого 'base_var' из описанного в 3.4.1 процесса исключается рассмотрение базового класса. Понятно, что при этом имя найдено не будет.
MS>Второй вопрос — если так, то почему резолвится this->base_var? Ведь this это не base_class<T>, это — тот же самый derived_class<T>?
'this->base_var' — зависимое выражение. Просто 'base_var' — независимое выражение. Отсюда и различия в разрешении имен, содержащихся в этих выражениях.
Здравствуйте, Павел Кузнецов, Вы писали:
>> По стандарту. И не только gcc, но и VC++ 7.1 делает то же самое с опцией /Za.
ПК>VC++ 7.1 не поддерживает two phase name lookup, и, насколько мне известно, следующая версия тоже не будет.
Тем не менее.
/* main.cpp */#include <iostream>
#include <strstream>
using namespace std;
template <class T>
struct Base
{
int i;
Base() : i(54321) {}
};
int i=12345;
template <class T>
struct Test : Base<T>
{
void test()
{
cout << "i=" << i << endl ;
}
};
/* main() */int main()
{
Test<int>().test();
return 0;
}
> ПК>VC++ 7.1 не поддерживает two phase name lookup, и, насколько мне известно, следующая версия тоже не будет. > > Тем не менее. <...>
Да, это первые шаги в нужном направлении. К сожалению, насколько я знаю, реализацию полноценного two phase lookup и в следующей версии было решено не делать.
Posted via RSDN NNTP Server 1.9 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Андрей Тарасевич, Вы писали:
MS>>Второй вопрос — если так, то почему резолвится this->base_var? Ведь this это не base_class<T>, это — тот же самый derived_class<T>?
АТ>'this->base_var' — зависимое выражение. Просто 'base_var' — независимое выражение. Отсюда и различия в разрешении имен, содержащихся в этих выражениях.
Спасибо!
Все-таки мне кажется более логичным писать типа:
Хоть и больше писанины, но гораздо нагляднее.
А вот для виртуальных функций — только "this->"
Кстати, попутный вопрос — я до сих пор пишу template<class T>... Не собираются ли они в будущем упразнить такую форму декларации и заставить писать template<typename T>?
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
> Кстати, попутный вопрос — я до сих пор пишу template<class T>... Не собираются ли они в будущем упразнить такую форму декларации и заставить писать template<typename T>?
Вряд ли: без серьезных оснований никто не решится ломать огромное количество написанного кода — а в данном случае никаких оснований запрещать template<class T> не видно.
Posted via RSDN NNTP Server 1.9 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен