Шаблонные классы против частного наследования - PullRequest
3 голосов
/ 18 октября 2011

Почему некоторые компиляторы настаивают на том, чтобы квалифицированные члены являлись открытыми членами базового класса шаблона, в то время как не требуется то же самое для не шаблонного класса? Пожалуйста, посмотрите на следующие списки кодов:

Шаблон класса:

#include <iostream>

using namespace std;

template <class T>
class TestImpl {
public: // It wont make a difference even if we use a protected access specifier here
    size_t vval_;
    TestImpl(size_t val = 0) : vval_(val) { }
};

template <class T>
class Test : public TestImpl<T> {
public:
    Test(size_t val) : TestImpl<T>(val) {
        cout << "vval_ : " << vval_ << endl; // Error: vval_ was not declared in this scope
        //! cout << "vval_ : " << TestImpl<T>::vval_ << endl; // this works, obviously
    }
};

int main() {
    Test<int> test1(7);

    return 0;
}

Не шаблонный класс:

#include <iostream>

using namespace std;

class TestImpl {
public: // It wont make a difference even if we use a protected access specifier here
    TestImpl(size_t val = 0) : vval_(val) {}
    size_t vval_;
};

class Test : public TestImpl {
public:
    Test(size_t val) : TestImpl(val) {
        cout << "vval_ : " << vval_ << endl;
    }
};

int main() {
    Test test1(7);

    return 0;
}

Существенная разница между приведенными выше списками кода заключается в том, что, хотя первый список использует классы шаблонов, второй - нет.

Теперь оба списка прекрасно скомпилируются с Компилятор Microsoft Visual Studio (cl), но первый список WONT компилируется с обоими Digital Mars Compiler (dmc ) и Минималистский GNU для Windows (MinGW - g ++) компилятор. Я получу ошибку типа "vval_ не был объявлен в области видимости" - ошибка, я, очевидно, понимаю, что это значит.

Если я получу доступ к публичной переменной TestImpl vval_ с использованием TestImpl :: vval_ , код работает. Во втором листинге компиляторы не жалуются, когда производный класс обращается к переменной базового класса ' vval_ , не квалифицируя ее.

Что касается двух компиляторов и, возможно, других, мой вопрос заключается в том, почему я должен иметь возможность прямого доступа (без квалификации) к vval_ переменной напрямую из не шаблонного класса, унаследованного от не шаблонный класс , хотя я не могу сделать то же самое с шаблонным классом, унаследованным от шаблонного класса ?

Ответы [ 3 ]

6 голосов
/ 18 октября 2011

Вы должны квалифицировать vval_ с помощью TestImpl<T>, чтобы сообщить компилятору, что это зависит от фактического типа T в Test<T> (могут быть некоторые частичные / явные специализации TestImpl<T>, объявленные доопределение Test<T> и его реализация, которая изменит значение vval_ в этом контексте. Чтобы компилятор знал об этом, вы должны сказать, что vval_ (параметр шаблона) зависит.

См. Также http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html

3 голосов
/ 18 октября 2011

MSVC (Microsoft ...) никогда не был стандартно совместимым, когда речь заходит о коде шаблона, поэтому он лишний:)

Проблема в том, что шаблоны анализируются в два этапа:

  • Первый этап выполняется, когда шаблон анализируется, и любой идентификатор, который не зависит (явно) от параметров шаблона, должен быть разрешен
  • Второй этап выполняется, когда создается экземпляр шаблона

В вашем случае первая фаза заканчивается неудачей, поскольку vval_ явно не зависит от параметра шаблона (не является зависимым именем), поэтому он должен быть доступен.

ПростоеСредство защиты - квалифицировать vval_, обычно с this->, чтобы пометить его как зависимого.

2 голосов
/ 18 октября 2011

Проблема, с которой вы сталкиваетесь, заключается в том, что для компилятора vval_ не является зависимым именем, поэтому он попытается найти его до фактической реализации шаблона с типом. В этот момент базовый тип еще не известен компилятору [*] , и поэтому он не учитывает шаблонные базы. Visual Studio не выполняет двухфазный поиск, и, следовательно, здесь это не требуется.

Решение заключается в преобразовании идентификатора в зависимый идентификатор, что можно сделать одним из нескольких способов. Самым простым и рекомендуемым будет использование this (как в this->vval_). При добавлении явного this компилятор знает, что vval_ может отличаться в зависимости от аргументов шаблона, теперь это зависимое имя , и оно откладывает поиск до второй фазы (после замены аргумента).

В качестве альтернативы, вы можете определить тип, к которому принадлежит идентификатор, как предлагает @mrozenau, используя TestImpl<T>::vval_. Опять же, это делает идентификатор зависимым от аргумента шаблона T, и поиск откладывается. Хотя оба они служат конечной цели - отложить поиск на более позднее время, у этого второго подхода есть дополнительный побочный эффект, заключающийся в том, что динамическая отправка будет отключена. В данном конкретном случае это не имеет значения, но если vval_ на самом деле является виртуальной функцией, то this->f() вызовет конечную переопределение, а TestImpl<T>::f() выполнит переопределение, присутствующее в TestImpl<T>.

[*] На первом этапе проверки шаблонов перед тем, как аргументы подставляются в шаблон, базовый тип еще не известен. Причина этого заключается в том, что разные наборы аргументов могут инициировать выбор разных специализаций базового шаблона.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...