Распространение 'typedef' от основанного до производного класса для 'template'
Я пытаюсь определить базовый класс, который содержит только typedef.
template<typename T>
class A
{
public:
typedef std::vector<T> Vec_t;
};
template<typename T>
class B : public A<T>
{
private:
Vec_t v; // fails - Vec_t is not recognized
};
Почему в B I появляется ошибка, что Vec_t не распознается и мне нужно написать это явно?
typename A<T>::Vec_t v;
7 ответов
Я считаю, что этот вопрос дублирует, но я не могу найти его сейчас. Стандарт C++ говорит, что вы должны полностью квалифицировать имя в соответствии с 14.6.2/3:
В определении шаблона класса или члена шаблона класса, если базовый класс шаблона класса зависит от параметра-шаблона, область действия базового класса не проверяется при поиске неквалифицированного имени ни в точке определения класса шаблон или член или во время создания шаблона класса или члена.
UPD: наконец-то нашел дубликат: вот оно.
В случае шаблонов есть так называемые зависимые и независимые имена.
Если имя зависит от параметра шаблона T, его зависимое имя и другие, которые не зависят от параметра T, являются независимыми именами.
Вот правило: компилятор не ищет зависимые базовые классы (например, A) при поиске независимых имен (например, Vec_t). В результате компилятор не знает, что они даже существуют, не говоря уже о типах.
Компилятор не может предположить, что Vec_t
это тип, пока он не знает T
Существует потенциальная специализация A<T>
где A<T>:: Vec_t
является членом данных
Таким образом, решение заключается в использовании Typename
typename A<T>::Vec_t v; ← good
Я рекомендую вам пройти этот https://isocpp.org/wiki/faq/templates.
Старая (неработающая) ссылка: http://www.parashift.com/c++-faq-lite/templates.html
Для полноты, вот как вы можете немного смягчить эту неприятность:
- повторно введите эти типы в производные классы или лучше - как в методах -
- просто импортируйте эти имена в область производного класса с
using declaration
:
template<typename T>
class A
{
public:
typedef std::vector<T> Vec_t;
};
template<typename T>
class B : public A<T>
{
public:
using typename A<T>::Vec_t;
// .........
private:
Vec_t v;
};
Это может быть полезно, если у вас есть несколько упоминаний о наследуемом typedef
в производном классе. Также вам не нужно добавлять typename
каждый раз с этим.
Потому что компилятор не уверен, что Vec_t
называет тип. Например, A<T>
может быть специализированным для T=int
не иметь этого конкретного typedef
,
Вы должны четко квалифицировать использование Vec_t
потому что компилятор не знает где Vec_t
происходит от.
Он не может ничего предположить о структуре A, поскольку шаблон класса A может быть специализированным. Специализация может включать в себя Vec_t
который не является typedef, или он может даже не включать член Vec_t
совсем.
Vec_t не является зависимым именем, и компилятор должен знать, что это такое, без создания каких-либо шаблонов (в данном случае базового класса). Это действительно ничем не отличается от:
template <class T>
class X
{
std::string s;
}
Здесь также необходимо, чтобы компилятор знал о std::string, даже если X не создан, поскольку имя не зависит от аргумента шаблона T (насколько может предположить компилятор).
В целом, typedefs в базовом классе шаблона кажутся довольно бесполезными для использования в производном классе. Однако typedefs полезны для пользователя.
Эта концепция может быть связана с тем, как мы используем std::vector<T>
, Например, если у нас есть std::vector<int> Foo
, Теперь мы решили использовать любой из его типов членов, скажем, iterator
, В этом сценарии мы явно упоминаем
std::vector<int>::iterator foo_iterator;
Точно так же в вашем случае, чтобы использовать открытый тип члена Vec_t
из template <typename T> class A
необходимо явно объявить
A<T>::Vec_t v;
OR
A<int>::Vec_t int_type;