Почему у производного класса шаблона нет доступа к идентификаторам базового класса шаблона? - PullRequest
42 голосов
/ 06 августа 2009

Рассмотрим:

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 ]

46 голосов
/ 06 августа 2009

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

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

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

29 голосов
/ 10 августа 2009

Проблема, с которой вы столкнулись, связана с правилами поиска имен для зависимых базовых классов. 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 &).

1 голос
/ 06 августа 2009

Кажется, компилируется нормально в 2008 году. Вы пробовали:

public:
    Derived( bool initZero = Base<T>::NO_ZEROFILL );
0 голосов
/ 14 апреля 2015

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

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

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