Распространение typedef из базового класса в производный для шаблона

Я пытаюсь определить базовый класс, который содержит только 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 я получаю ошибку о том, что Vec_t не распознается и мне нужно это явно написать?

typename A<T>::Vec_t v;

person dimba    schedule 29.10.2009    source источник
comment
Точный дубликат: stackoverflow.com/questions/1567730/   -  person Kirill V. Lyadvinsky    schedule 29.10.2009
comment
Ну, не совсем точный дубликат, поскольку в сообщении, которое вы упоминаете, говорится о методе, а в этом - о типе.   -  person Matthieu M.    schedule 29.10.2009
comment
typename A :: Vec_t v; Это хорошо. Нет необходимости в ‹T› там   -  person Johan Lundberg    schedule 17.08.2012


Ответы (7)


Я считаю, что это повторяющийся вопрос, но сейчас не могу его найти. Стандарт C ++ говорит, что вы должны полностью указать имя в соответствии с 14.6.2 / 3:

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

UPD: наконец-то я нашел дубликат: вот он.

person Kirill V. Lyadvinsky    schedule 29.10.2009
comment
Кстати, меня всегда мучило то, что мне приходилось все «перепечатывать» ... это неприятно, совсем не приятно. - person Matthieu M.; 29.10.2009
comment
кстати, при квалификации вам не нужны все аргументы шаблона и все такое. Из-за введенного имени класса достаточно написать typename B::Vec_t - person Johannes Schaub - litb; 30.10.2009
comment
@ JohannesSchaub-litb Я пытаюсь сделать, как вы сказали, но получаю ошибки, если не указываю параметры шаблона для B. (B не является классом, пространством имен или перечислением) - person Gonzalo Solera; 06.08.2018
comment
@GonzaloSolera У меня такой же результат ошибки, но только на одной из моих платформ, на одной со старым стандартом C ++. Интересно, изменилась ли потребность в полной квалификации в какой-то момент? - person srm; 04.01.2019

В случае шаблонов есть так называемые зависимые и независимые имена.

Если имя зависит от параметра шаблона 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#nondependent-name-lookup-types.

Старая (неработающая) ссылка: http://www.parashift.com/c++-faq-lite/templates.html#faq-35.18

person Xinus    schedule 29.10.2009
comment
Ваш был единственным ответом, который, казалось, предлагал решение в дополнение к объяснению. Спасибо. - person Richard; 02.05.2013
comment
Предлагаю вам +1, если вы сообщите мне, что исправили ссылку. - person Jonathan Mee; 01.01.2016

Поскольку компилятор не уверен, что Vec_t называет тип. Например, A<T> может быть специализирован для T=int, чтобы не иметь этот конкретный typedef.

person Jesse Beder    schedule 29.10.2009
comment
Для T переменной типа A<T> просто объявлено вперед, у него нет определения. Только A<t>, где t - тип (не переменная типа), может быть определен (путем специализации определения шаблона или явной специализации). IOW, даже если вы удалите явную и частичную специализацию шаблона из C ++ (и не измените ничего другого), это все равно будет неверным. - person curiousguy; 16.08.2012

Для полноты, вот как вы могли бы немного уменьшить эту неприятность:

  • переопределите эти типы в производных классах или лучше - как с методами -
  • просто импортируйте эти имена в область производного класса с 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.

person Roman Kruglov    schedule 19.09.2018
comment
У вас опечатка. using typename A::Vec_t; должно быть using typename A<T>::Vec_t; - person NathanOliver; 06.11.2018

Вам необходимо явно указать использование Vec_t, потому что компилятор не знает, откуда берется Vec_t.

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

person user200783    schedule 29.10.2009

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

template <class T>
class X
{
    std::string s;
}

Здесь также компилятор должен знать о std :: string, даже если X не создается, поскольку имя не зависит от аргумента шаблона T (насколько компилятор может предположить).

В общем, typedef в базовом классе шаблона кажется бесполезным для использования в производном классе. Однако определения типов полезны для пользователя.

person UncleBens    schedule 29.10.2009
comment
Вы имеете в виду class X : T { здесь? - person curiousguy; 16.08.2012

Эта концепция может быть связана с тем, как мы используем 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;
person RAD    schedule 21.02.2018