Почему производный класс шаблона не имеет доступа к идентификаторам базового класса шаблона?

считаем:

template <typename T>
class Base
{
    public:
        static const bool ZEROFILL = true;
        static const bool NO_ZEROFILL = false;
}

template <typename T>
class Derived : public Base<T>
{
    public: 
        Derived( bool initZero = NO_ZEROFILL );    // NO_ZEROFILL is not visible
        ~Derived();
}

Я не могу скомпилировать это с GCC g++ 3.4.4 (cygwin).

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

Я понимаю, что каждый экземпляр Base<T> будет иметь собственный статический член"ZEROFILL" и "NO_ZEROFILL", это Base<float>::ZEROFILL и Base<double>::ZEROFILL разные переменные, но мне все равно; константа существует для читаемости кода. Я хотел использовать статическую константу, потому что это более безопасно с точки зрения конфликтов имен, а не макроса или глобального.

4 ответов


это двухфазный поиск для вас.

Base<T>::NO_ZEROFILL (все идентификаторы caps boo, за исключением макросов, BTW) - это идентификатор, который зависит от T.
Поскольку, когда компилятор сначала анализирует шаблон, нет никакого фактического типа, заменяющего T тем не менее, компилятор не "знает", что Base<T> - это. Поэтому он не может знать никаких идентификаторов, которые вы предполагаете определить в нем (может быть специализация для некоторых Ts, что компилятор видит только позже) и вы не можете опустить квалификацию базового класса из идентификаторов, определенных в базовом классе.

вот почему вы должны писать Base<T>::NO_ZEROFILL (или this->NO_ZEROFILL). Это говорит компилятору, что NO_ZEROFILL что-то в базовом классе, от которого зависит T, и что он может проверить его только позже,когда шаблон будет создан. Поэтому он примет его, не пытаясь проверить код.
Этот код может быть проверен только позже, когда шаблон создается путем предоставления фактического параметра для T.


проблема, с которой вы столкнулись, связана с правилами поиска имен для зависимых базовых классов. 14.6/8 имеет:

при поиске объявления имени, используемого в определении шаблона, обычные правила поиска (3.4.1, 3.4.2) используются для независимых имен. Поиск имен, зависящих от параметров шаблона отложено до фактического аргумента шаблона известен (14.6.2).

(на самом деле это не "2-фазный поиск" - см. ниже объяснение этому.)

пункт о 14.6 / 8 заключается в том, что, насколько это касается компилятора NO_ZEROFILL в вашем примере является идентификатором и не зависит от параметра шаблона. Поэтому он рассматривается в соответствии с обычными правилами в 3.4.1 и 3.4.2.

этот нормальный поиск не ищет внутри Base<T> и поэтому NO_ZEROFILL-это просто необъявленный идентификатор. 14.6.2/3 имеет:

в определении шаблона класса или член шаблон класса, если базовый класс шаблона класса в зависимости от параметра-шаблона область базового класса не рассматривается при поиске неквалифицированного имени либо в момент определения шаблона или члена класса, либо во время создания экземпляра шаблона класса или член.

когда вы имеете право NO_ZEROFILL С Base<T>:: по сути, вы меняете его с не зависимого имени на зависимое, и когда вы это делаете, вы задерживаете его поиск до шаблона инстанцируют.

боковое примечание: что такое 2-фазный поиск:

void bar (int);

template <typename T>
void foo (T const & t) {
  bar (t);
}


namespace NS
{
  struct A {};
  void bar (A const &);
}


int main ()
{
  NS::A a;
  foo (a);
}

приведенный выше пример компилируется следующим образом. Компилятор анализирует тело функции foo и посмотрите, что есть вызов bar который имеет зависимый аргумент (т. е. тот, который зависит от параметра template). На этом этапе компилятор ищет панель в соответствии с 3.4.1, и это "поиск фазы 1". Поиск найдет функцию void bar (int) и это хранится с зависимый звонок позже.

когда шаблон затем создается экземпляр (в результате вызова из main), компилятор затем выполняет дополнительный поиск в области аргумента, это "поиск фазы 2". Этот случай, который приводит к поиску void NS::bar(A const &).

компилятор имеет две перегрузки для bar и он выбирает между ними, в приведенном выше случае вызова void NS::bar(A const &).


Кажется, что компиляция ok в vs 2008. Вы пробовали:

public:
    Derived( bool initZero = Base<T>::NO_ZEROFILL );

попробуйте эту программу

#include<iostream>
using namespace std;
template <class T> class base{
public:
T x;
base(T a){x=a;}
virtual T get(void){return x;}
};
template <class T>
class derived:public base<T>{
public:
derived(T a):base<T>(a){}
T get(void){return this->x+2;}
};
int main(void){
base<int> ob1(10);
cout<<ob1.get()<<endl;
derived<float> ob(10);
cout<<ob.get();
return 0;
}

на T get(void){return this->x+2;} line u также может использовать оператор разрешения области (::). например, попробуйте заменить строку с

T get(void){return base<T>::x+2;}