Где и зачем нужно использовать ключевые слова "template" и "typename"?
В шаблонах, где и почему мне нужно указывать typename
и template
для зависимых имен? Что такое зависимые имена?
У меня есть следующий код:
template <typename T, typename Tail> // Tail тоже будет UnionNode.
struct UnionNode : public Tail {
// ...
template <typename U> struct inUnion {
// Вопрос: где нужно добавить typename/template здесь?
typedef Tail::inUnion<U> dummy;
};
template <> struct inUnion<T> { };
};
template <typename T> // Для последнего узла Tn.
struct UnionNode<T, void> {
// ...
template <typename U> struct inUnion; // намеренно не определено
template <> struct inUnion<T> { }; // специальная версия только для T
};
Проблема заключается в строке typedef Tail::inUnion dummy
. Я довольно уверен, что inUnion
является зависимым именем, и VC++ абсолютно прав, что не может это обработать.
Я также знаю, что я должен смочь добавить template
где-то, чтобы указать компилятору, что inUnion
является шаблоном, но где именно? Нужно ли тогда предположить, что inUnion
— это шаблон класса, т.е. inUnion
называет тип, а не функцию?
2 ответ(ов)
Общее правило для использования ключевого слова typename
заключается в том, что его нужно добавлять, когда вы работаете с параметром шаблона и хотите получить доступ к вложенному typedef
или alias, например:
template<typename T>
struct test {
using type = T; // typename не требуется
using underlying_type = typename T::type; // typename требуется
};
Обратите внимание, что это также касается метафункций или вещей, которые принимают обобщенные параметры шаблона. Однако, если предоставленный параметр шаблона является явным типом, тогда указывать typename
не нужно, например:
template<typename T>
struct test {
// typename требуется
using type = typename std::conditional<true, const T&, T&&>::type;
// typename не требуется
using integer = std::conditional<true, int, float>::type;
};
Общие правила для добавления квалификатора template
в основном схожи, однако они обычно связаны с шаблонными методами (статическими или нет) структуры/класса, который сам является шаблоном, например:
Учитывая эту структуру и функцию:
template<typename T>
struct test {
template<typename U>
void get() const {
std::cout << "get\n";
}
};
template<typename T>
void func(const test<T>& t) {
t.get<int>(); // ошибка
}
Попытка вызвать t.get<int>()
из функции приведет к ошибке:
main.cpp:13:11: error: expected primary-expression before 'int'
t.get<int>();
^
main.cpp:13:11: error: expected ';' before 'int'
Таким образом, в этом контексте вам необходимо предварительно указать ключевое слово template
и вызвать его так:
t.template get<int>()
Таким образом, компилятор правильно обработает это выражение, а не воспримет его как t.get < int
.
Вопрос: Что такое зависимые имена в шаблонах C++ и как их правильно использовать?
Ответ: Зависимые имена — это имена, которые зависят от параметров шаблона. Когда мы работаем с шаблонами, компилятору нужно указать, как правильно интерпретировать эти имена до момента их фактической инстанциации. В C++ это реализуется с помощью ключевых слов typename
и template
.
typename
— сообщает компилятору, что зависимое имя является фактическим типом.template <class T> struct DependentType { typename T::type a; // Указываем, что T::type - это тип using Type = typename T::type; // Используем using для создания псевдонима типа };
template
— указывает компилятору, что зависимое имя является шаблонной функцией или классом.template <class T> struct DependentTemplate { // Шаблонная функция template <class U> static void func() {} // Шаблонный класс template <class U> struct ClassName{}; }; template <class T1, class T2> void foo() { // 3 способа вызвать зависимую шаблонную функцию DependentTemplate<T1>::template func<T2>(); // Используем `template` DependentTemplate<T1>().template func<T2>(); // Используем `template` (new DependentTemplate<T1>())->template func<T2>(); // Используем `template` // Для ссылки на зависимый шаблонный класс нужно использовать и `typename`, и `template` typename DependentTemplate<T1>::template ClassName<T2> obj; // Используем оба ключевых слова using Type = typename DependentTemplate<T1>::template ClassName<T2>; // Псевдоним типа }
Использование этих ключевых слов позволяет компилятору корректно определить и обработать зависимые имена в шаблонах.
Что такое Правило трёх?
Разница между const int*, const int * const и int * const?
Новые возможности C++17: что стоит знать?
Имеют ли круглые скобки после имени типа значение при использовании new?
Что означает 'const' в конце объявления метода класса?