Перейти к содержимому

Typename c что это

  • автор:

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

Станислав Макаров @Nipheris Куратор тега C++

1. typename в данном случае нужен компилятору только как подсказка от разработчика, что последующий идентификатор (т.е. std::stack::container_type::iterator ) - это действительно имя типа. Подсказка нужна потому, что этот typedef вероятно находится также в шаблоне, и мы ещё не знаем, во что конкретно инстанциируется шаблон std::stack (в этом случае говорят, что container_type "is dependent on a template-parameter" - пока не инстанциируем std::stack, не узнаем).

2. Member-тип container_type эквивалентен типу нижележащего контейнера (т.к. std::stack - это адаптер под интерфейс стека, а не реальный контейнер, реальный контейнер для хранения вы выбираете вторым параметром шаблона, по-умолчанию это std::deque).

3. Вот у std::deque итератор действительно есть.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *