Перейти к содержанию

Последнее обновление:2026-10-12

Анализ исходников STL typename

В исходниках STL вниз встречаются такие строки — typedef в сочетании с typename; они выглядят очень сложными. Что же они значат? Давайте сегодня разберём это вместе!

template<typename _Iterator>
struct iterator_traits
{
  typedef typename _Iterator::iterator_category iterator_category;
  typedef typename _Iterator::value_type        value_type;
  typedef typename _Iterator::difference_type   difference_type;
  typedef typename _Iterator::pointer           pointer;
  typedef typename _Iterator::reference         reference;
};

Распространённые способы использования typename

Сначала изучим типичные способы использования typename:

template <typename T>
int compare(const T &a, const T &b)
{
    return a>b?a:b;
}

Выше — просто учебный пример; для полноценного сравнения пришлось бы учесть и специализированные версии. Возможно, вы заметили, что typename в приведённом коде можно заменить на class — и всё будет работать. Верно! Тогда возникает вопрос: есть ли разница между этими двумя способами? Если заглянуть в C++ Primer, выяснится, что они совершенно одинаковы.

Область видимости класса

При обращении извне класса к именам внутри класса можно использовать оператор области видимости класса. Вызовы вида MyClass::name обычно бывают трёх видов: статические данные-члены, статические функции-члены и вложенные типы:

struct MyClass {
    static int A; //静态成员
    static int B(){cout<<"B()"<<endl; return 100;} //静态函数
    typedef int C;  //嵌套类型
    struct A1 { //嵌套类型
        static int s;
    };
};

При вызове можно писать напрямую:

cout<<MyClass::A<<endl;
cout<<MyClass::B()<<endl;
MyClass:C c;
...

Попробуем полный пример

Вернёмся к примеру с typename:

template <class T>
void foo() {
    T::iterator * iter;
    // ...
}

Какова цель этого кода? Первая реакция большинства: автор хотел определить указатель iter, указывающий на тип iterator, находящийся в области видимости класса T. Возможна такая структура, содержащая тип iterator:

struct MyIterator {
    struct iterator {

    };
};

Вызов такой:

foo<MyIterator>();

Теперь строка с iter совершенно очевидна: это указатель типа MyIterator::iterator. Мы предполагаем, что дело обстоит так, — а так ли это на самом деле?

Но что если это нечто вроде T::iterator? T — параметр типа в шаблоне, и какой это будет тип, станет известно лишь при инстанцировании шаблона, не говоря уже о внутреннем iterator. Из приведённого выше рассказа об области видимости класса мы знаем, что T::iterator на деле может быть любым из трёх вариантов:

  • статический данные-член
  • статическая функция-член
  • вложенный тип

В предыдущем примере ContainsAType::iterator — вложенный тип, и здесь всё в порядке. А если это статические данные-член? Что если тип, которым инстанцируется шаблонная функция foo, выглядит так:

struct MyIterator {
    static int iterator;
};

Тогда T::iterator * iter; компилятор инстанцирует как MyIterator::iterator * iter; — а это что? Слева статическая переменная-член, а не тип, и выражение превращается в умножение; правда, iter здесь не определён, и компилятор выдаст ошибку:

error: no type named ‘iterator’ in ‘struct MyIterator’

typename

Имя, используемое в определении шаблона и зависящее от параметра шаблона, компилятор будет считать типом, только если такой тип существует среди параметров инстанцирования или если перед именем стоит модификатор — ключевое слово typename. Кроме этих двух случаев имя ни в коем случае не будет считаться типом.

Поэтому, если вы хотите прямо сообщить компилятору, что T::iterator — это тип, а не переменная, достаточно добавить typename:

template <class T>
void foo() {
    typename T::iterator * iter;
}

Теперь компилятор может определить, что T::iterator — тип, уже не дожидаясь момента инстанцирования; тем самым устраняется упомянутая выше неоднозначность.

Разбор исходников

Вернёмся к исходникам STL

template<typename _Iterator>
struct iterator_traits
{
  typedef typename _Iterator::iterator_category iterator_category;
  typedef typename _Iterator::value_type        value_type;
  typedef typename _Iterator::difference_type   difference_type;
  typedef typename _Iterator::pointer           pointer;
  typedef typename _Iterator::reference         reference;
};

Увидев это, мы сразу всё понимаем: здесь попросту с помощью typename сообщается, что _Iterator::iterator_category — тип, а затем typedef даёт ему новое имя; остальное аналогично!

Похожие темы

Комментарии