13

Где и зачем нужно использовать ключевые слова "template" и "typename"?

14

В шаблонах, где и почему мне нужно указывать 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 ответ(ов)

0

Общее правило для использования ключевого слова 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.

0

Вопрос: Что такое зависимые имена в шаблонах C++ и как их правильно использовать?

Ответ: Зависимые имена — это имена, которые зависят от параметров шаблона. Когда мы работаем с шаблонами, компилятору нужно указать, как правильно интерпретировать эти имена до момента их фактической инстанциации. В C++ это реализуется с помощью ключевых слов typename и template.

  1. typename — сообщает компилятору, что зависимое имя является фактическим типом.

    template <class T>
    struct DependentType
    {
       typename T::type a;         // Указываем, что T::type - это тип
       using Type = typename T::type; // Используем using для создания псевдонима типа
    };
    
  2. 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>; // Псевдоним типа
    }
    

Использование этих ключевых слов позволяет компилятору корректно определить и обработать зависимые имена в шаблонах.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь