Ответ
Ключевое слово typename в C++ имеет две основные роли, и обе связаны с шаблонами.
1. Объявление параметра шаблона типа (синоним class)
В этом контексте typename и class взаимозаменяемы.
template <typename T> // Эквивалентно template <class T>
T max(T a, T b) { return (a > b) ? a : b; }
2. Указание, что зависимое имя является ТИПОМ (главное назначение)
Это критически важное использование. Внутри шаблонного кода компилятор по умолчанию не знает, является ли имя, зависящее от параметра шаблона (T::something), типом или чем-то иным (например, статической переменной). typename устраняет эту неоднозначность.
Пример проблемы и решения:
template <typename T>
class Container {
T::iterator* ptr1; // ОШИБКА КОМПИЛЯЦИИ! Компилятор думает:
// 'iterator' — это статическое поле класса T?
// Тогда это выражение — умножение (T::iterator) * (ptr1)?
typename T::iterator* ptr2; // КОРРЕКТНО. Явно говорим, что T::iterator — это тип,
// значит, это объявление указателя.
};
Типичные случаи, где требуется typename:
-
Объявление переменных зависимого типа:
template <typename Iter> void process(Iter begin, Iter end) { typename std::iterator_traits<Iter>::value_type temp = *begin; // Без 'typename' компилятор не поймет, что 'value_type' — это тип. } -
Указание возвращаемого типа, который является зависимым:
template <typename C> typename C::value_type getFirst(const C& container) { if (!container.empty()) return *container.begin(); return typename C::value_type{}; // Также требуется здесь. } -
В списках инициализации базовых классов и членов (C++17
std::void_t, метапрограммирование).
Правило: Ключевое слово typename должно предшествовать полностью квалифицированному имени, которое зависит от параметра шаблона и является типом. Для независимых имен или известных типов (например, int, std::string) оно не нужно.