Подтвердить что ты не робот

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

Рассмотрим:

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

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

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

Боковое примечание: что такое двухфазный поиск:

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, который имеет зависимый аргумент (то есть тот, который зависит от параметра шаблона). В этот момент компилятор просматривает панель согласно 3.4.1, и это "поиск по фазе 1". Поиск найдет функцию void bar (int) и будет сохранен с зависимым вызовом до более позднего времени.

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

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

Ответ 3

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

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

Ответ 4

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

#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;} u также может использовать оператор разрешающей способности (:). например, попробуйте заменить строку на

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