typename
В определениях шаблонов typename предоставляется указание компилятору о том, что неизвестный идентификатор является типом. В списках параметров шаблона используется для указания параметра типа.
Синтаксис
typename identifier ;
Замечания
typename Ключевое слово необходимо использовать, если имя в определении шаблона — это полное имя, зависящее от аргумента шаблона; необязательно, если полное имя не зависит. Дополнительные сведения см. в разделе «Шаблоны» и «Разрешение имен».
typename может использоваться любым типом в любом месте объявления или определения шаблона. Он не допускается в списке базовых классов, если только не в качестве аргумента шаблона в базовый класс шаблона.
template class C1 : typename T::InnerType // Error - typename not allowed. <>; template class C2 : A // typename OK. <>;
Ключевое слово typename также можно использовать вместо class списков параметров шаблона. Например, следующие операторы семантически эквивалентны:
template. template.
Пример
// typename.cpp template class X < typename T::Y m_y; // treat Y as a type >; int main()
Зачем надо писать typename для уточнения типа?
@fori1ton: там не аналогичный пример. Насколько я понял, ОП спрашивает зачем в этом коде сделан typename.
25 июл 2013 в 13:16
Подробное описание находится по ссылке представленной @fori1ton. Если кратко, то компилятор не знает, что означает const_iterator . Синтаксис схож как для переменной, так и для объявления типов ( typedef ). Оператор typename говорит, что это имя типа (не переменная).
25 июл 2013 в 13:17
@Expert: а что может означать строка T::const_iterator pos кроме как не объявление типа? Не T::const_iterator * pos , а именно T::const_iterator pos ? Компилятор не знает что именно такое T::const_iterator, но что здесь можно предположить как не тип? Помоему в этом вопрос заключается. Сам я склоняюсь к тому, что это, если не ошибаюсь, требование стандарта.
25 июл 2013 в 13:25
@fori1ton, читал ещё давно, но тут же нету звездочки как подметил @fogbit. То есть компилятор не может определить какой тип туда попадет и это делается в рантайм? Но даже если и так, то без typename выйдет же T(тип)::const_iterator(имя переменной) pos(имя переменной), но такое объявление вызовет ошибку. Если есть строчка в стандарте, в которой сказано о необходимости typename в такой ситуации, то зачем она нужна? Ведь компилятор именно в этом случае может и сам справится.
25 июл 2013 в 14:08
@Robert Pinkman: непосредственно тип выводится в момент инстанса шаблона в коде, т.е. в compile-time. C шаблонами вообще всё происходит в compile-time, если не ошибаюсь. Подозреваю что Стандарт не делает различий в ситуациях, а просто декларирует что если есть объявление типа, зависящего от шаблона в любом контексте, то пиши перед ним typename .
25 июл 2013 в 14:45
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Проблема в том, что T::const_iterator — зависимое имя: оно зависит от параметра шаблона T . В этой точке компилятор не знает, каким будет T , и не может предсказать, будет ли T::const_iterator именем типа или, например, именем статического поля или вообще шаблона. Поэтому он и не пытается угадать, и предполагает, что это поле.
Если же ему подсказать, он будет предполагать, что T::const_iterator — это тип, и поймёт, что
typename T::const_iterator pos;
Почему же компилятор не может подождать с выяснением смысла выражения T::const_iterator до того момента, когда тип T будет уже известен (то есть, до момента разворачивания шаблона с конкретным типом T )? А вот почему: на момент применения шаблона тип T имеет право быть ещё не определён! И ещё он может зависеть от самого шаблона. Так что откладывать выяснение смысла выражения нельзя. Пример:
template class comparable < bool compare(T& other) < return this == other; >>; class length : public comparable // в этой точке для comparable // тип T ещё не известен полностью! < .
Пример кода, иллюстрирующего «скользкие» моменты, приведён ниже. Он не компилируется gcc (так как нету полагающегося по стандарту typename ), но более либеральный MSVC 2012 его компилирует и выполняет.
typename для того и нужен, чтобы исключить подобные сюрпризы.
#include "stdafx.h" // нужно для MSVC #include using namespace std; template struct A < void f() < // если T::iterator - тип, это предварительное объявление функции // если T::iterator - число, это объявление переменной с инициализацией int x(T::iterator); >void g() < int x = 5; < // если T::iterator - шаблон, принимающий числовой аргумент, // это инстанциация шаблона в переменную x, перекрывающую x // за фигурными скобками // если T::iterator -- экземпляр класса с перегруженным оператором x; // Кто-то всё ещё сомневается, что C++ - непредсказуемый язык? > > >; struct T1 < typedef int iterator; >; struct T2 < static const int iterator = 5; >; struct T3 < templatestruct iterator < iterator() < cout >; >; struct T4 < struct Titerator < Titerator operator < (int value) < cout bool operator > (int value) < cout " >; static Titerator iterator; >; T4::Titerator T4::iterator = T4::Titerator(); int main(int argc, char* argv[]) < Aa1; a1.f(); A a2; a2.f(); A a3; a3.g(); A a4; a4.g(); return 0; >
Результат работы таков:
constructing template with C = 0
in operator < 0
in operator > 5
Шаблоны классов в С++
Мы уже ранее рассматривали в С++ такой инструмент, как шаблоны, когда создавали шаблоны функций. Почему стоит пользоваться шаблонами, было написано в статье, с шаблонами функций. Там мы рассмотрели основные положения шаблонов в С++. Давайте их вспомним.
Любой шаблон начинается со слова template , будь то шаблон функции или шаблон класса. После ключевого слова template идут угловые скобки — < >, в которых перечисляется список параметров шаблона. Каждому параметру должно предшествовать зарезервированное слово class или typename . Отсутствие этих ключевых слов будет расцениваться компилятором как синтаксическая ошибка. Некоторые примеры объявления шаблонов:
template
template
template
Ключевое слово typename говорит о том, что в шаблоне будет использоваться встроенный тип данных, такой как: int , double , float , char и т. д. А ключевое слово class сообщает компилятору, что в шаблоне функции в качестве параметра будут использоваться пользовательские типы данных, то есть классы. Но не в коем случае не путайте параметр шаблона и шаблон класса. Если нам надо создать шаблон класса, с одним параметром типа int и char , шаблон класса будет выглядеть так:
template class Name < //тело шаблона класса >;
где T — это параметр шаблона класса, который может принимать любой из встроенных типов данных, то, что нам и нужно.
А если параметр шаблона класса должен пользовательского типа, например типа Array , где Array — это класс, описывающий массив, шаблон класса будет иметь следующий вид:
template class Name < //тело шаблона класса >;
C этим вам лучше разобраться изначально, чтобы потом не возникало никаких ошибок, даже, если шаблон класса написан правильно.
Давайте создадим шаблон класса Стек, где стек — структура данных, в которой хранятся однотипные элементы данных. В стек можно помещать и извлекать данные. Добавляемый элемент в стек, помещается в вершину стека. Удаляются элементы стека, начиная с его вершины. В шаблоне класса Stack необходимо создать основные методы:
- Push — добавить элемент в стек;
- Pop — удалить элемент из стека
- printStack — вывод стека на экран;
Итак реализуем эти три метода, в итоге получим самый простой класс, реализующий работу структуры стек. Не забываем про конструкторы и деструкторы. Смотрим код ниже.
#include "stdafx.h" #include using namespace std; #include template class Stack < private: T *stackPtr; // указатель на стек int size; // размер стека T top; // вершина стека public: Stack(int = 10);// по умолчанию размер стека равен 10 элементам ~Stack(); // деструктор bool push(const T ); // поместить элемент в стек bool pop(); // удалить из стека элемент void printStack(); >; int main() < Stack myStack(5); // заполняем стек cout > temp; myStack.push(temp); > myStack.printStack(); // вывод стека на экран cout // конструктор template Stack::Stack(int s) < size = s >0 ? s: 10; // инициализировать размер стека stackPtr = new T[size]; // выделить память под стек top = -1; // значение -1 говорит о том, что стек пуст > // деструктор template Stack::~Stack() < delete [] stackPtr; // удаляем стек >// элемент функция класса Stack для помещения элемента в стек // возвращаемое значение - true, операция успешно завершена // false, элемент в стек не добавлен template bool Stack::push(const T value) < if (top == size - 1) return false; // стек полон top++; stackPtr[top] = value; // помещаем элемент в стек return true; // успешное выполнение операции >// элемент функция класса Stack для удаления элемента из стек // возвращаемое значение - true, операция успешно завершена // false, стек пуст template bool Stack::pop() < if (top == - 1) return false; // стек пуст stackPtr[top] = 0; // удаляем элемент из стека top--; return true; // успешное выполнение операции >// вывод стека на экран template void Stack::printStack() < for (int ix = size -1; ix >= 0; ix--) cout
#include using namespace std; #include template class Stack < private: T *stackPtr; // указатель на стек int size; // размер стека T top; // вершина стека public: Stack(int = 10);// по умолчанию размер стека равен 10 элементам ~Stack(); // деструктор bool push(const T ); // поместить элемент в стек bool pop(); // удалить из стека элемент void printStack(); >; int main() < Stack myStack(5); // заполняем стек cout > temp; myStack.push(temp); > myStack.printStack(); // вывод стека на экран cout // конструктор template Stack::Stack(int s) < size = s >0 ? s: 10; // инициализировать размер стека stackPtr = new T[size]; // выделить память под стек top = -1; // значение -1 говорит о том, что стек пуст > // деструктор template Stack::~Stack() < delete [] stackPtr; // удаляем стек >// элемент функция класса Stack для помещения элемента в стек // возвращаемое значение - true, операция успешно завершена // false, элемент в стек не добавлен template bool Stack::push(const T value) < if (top == size - 1) return false; // стек полон top++; stackPtr[top] = value; // помещаем элемент в стек return true; // успешное выполнение операции >// элемент функция класса Stack для удаления элемента из стек // возвращаемое значение - true, операция успешно завершена // false, стек пуст template bool Stack::pop() < if (top == - 1) return false; // стек пуст stackPtr[top] = 0; // удаляем элемент из стека top--; return true; // успешное выполнение операции >// вывод стека на экран template void Stack::printStack() < for (int ix = size -1; ix >= 0; ix--) cout
Как видите шаблон класса Stack объявлен и определен в файле с main -функцией. Конечно же такой способ утилизации шаблонов никуда не годится, но для примера сойдет. В строках 7 — 20 объявлен интерфейс шаблона класса. Объявление класса выполняется привычным для нас образом, а перед классом находится объявление шаблона, в строке 7. При объявлении шаблона класса, всегда используйте такой синтаксис.
Строки 47 — 100 содержат элемент-функции шаблона класса Stack, причем перед каждой функцией необходимо объявлять шаблон, точно такой же, как и перед классом — template . То есть получается, элемент-функции шаблона класса, объявляются точно также, как и обычные шаблоны функций. Если бы мы описали реализацию методов внутри класса, то заголовок шаблона — template для каждой функции прописывать не надо.
Чтобы привязать каждую элемент-функцию к шаблону класса, как обычно используем бинарную операцию разрешения области действия — :: с именем шаблона класса — Stack . Что мы и сделали в строках 49, 58, 68, 83, 96.
Обратите внимание на объявление объекта myStack шаблона класса Stack в функции main , строка 24. В угловых скобочка необходимо явно указывать используемый тип данных, в шаблонах функций этого делать не нужно было. Далее в main запускаются некоторые функции, которые демонстрируют работу шаблона класса Stack . Результат работы программы смотрим ниже.
CppStudio.com
Заталкиваем элементы в стек: 12 3456 768 5 4564 |4564 | 5 | 768 |3456 | 12 Удаляем два элемента из стека: | 0 | 0 | 768 |3456 | 12
К сожалению, для данной темы пока нет подходящих задач. Если у вас есть таковые на примете, отправте их по адресу: admin@cppstudio.com. Мы их опубликуем!
Что значит typename?
typedef я знаю.
typename - не уверен что понимаю это ключевое слово. Вне template как я понял используется для помощи компилятору в определении типа.
а что происходит дальше std::stack::container_type::iterator ? разве у стека есть итератор?
- Вопрос задан более двух лет назад
- 507 просмотров
Комментировать
Решения вопроса 1

Станислав Макаров @Nipheris Куратор тега C++
1. typename в данном случае нужен компилятору только как подсказка от разработчика, что последующий идентификатор (т.е. std::stack
2. Member-тип container_type эквивалентен типу нижележащего контейнера (т.к. std::stack - это адаптер под интерфейс стека, а не реальный контейнер, реальный контейнер для хранения вы выбираете вторым параметром шаблона, по-умолчанию это std::deque).
3. Вот у std::deque итератор действительно есть.