Программирование >>  Разработка устойчивых систем 

1 ... 54 55 56 [ 57 ] 58 59 60 ... 196


Определение шаблона предполагает, что в передаваемом ему классе Т должен содержаться некий вложенный идентификатор с именем id. Однако id также может быть статической переменной Т; в этом случае вы сможете напрямую выполнять операции с id, но не сможете создать объект типа id . В приведенном примере идентификатор id интерпретируется как вложенный тип по отношению к Т. В случае класса Y идентификатор id действительно является вложенным типом, но без ключевого слова typename компилятор не будет знать об этом при компиляции X.

Если компилятор, встречая идентификатор в шаблоне, может интерпретировать его как тип или как нечто иное, он всегда выбирает нечто иное . Иначе говоря, компилятор будет считать, что идентификатор относится к объекту (включая переменные примитивных типов), перечисляемому типу или чему-нибудь в этом роде. Но он никогда не будет - а точнее, не сможет - интерпретировать его как тип.

Раз компилятор по умолчанию предполагает, что потенциально неоднозначное имя не является типом, необходимо использовать ключевое слово typename для вложенных типов (кроме списков инициализаторов в конструкторах, где это ключевое слово не только не нужно, но и недопустимо). Когда в предыдушем примере компилятор встречает конструкцию typename T::id, он знает (благодаря ключевому слову typename), что идентификатор id относится к вложенному типу, и что он может создать объект этого типа.

Короче говоря, если тип, на который вы ссылаетесь в коде шаблона, уточняется параметром-шаблоном, обязательно используйте префикс typename везде, кроме спецификаций базового класса или списка инициализаторов в той же области видимости.

Предыдуший пример поясняет, для чего нужно ключевое слово typename в программе TempTemp4.cpp. Без него компилятор считает, что выражение Seq<T>::iterator не является типом, но мы используем его для определения возвращаемых типов функций begin() и end().

Аналогичное применение typename продемонстрировано в следующем примере. Эта программа выводит содержимое любого стандартного последовательного контейнера С++:

: C05:PrintSeq.cpp {-msc}{-mwcc}

Функция вывода стандартных последовательных контейнеров С++

#1 nclude <iostream>

#1nclude <list>

#1nclude <memory>

#1 nclude <vector>

using namespace std:

tempiate<class T. tempiate<class U. class = allocator<U> >

class Seq> void printSeq(Seq<T>& seq) { for (typename Seq<T>::iterator b = seq.beginO: b != seq.endO:) cout *b++ endl:

int mainO { Вывод содержимого вектора vector<int> v:



v.push back(l): v.push back(2): printSeq(v):

Вывод содержимого списка list<int> 1st: 1st.push back(3): 1st.push back(4): printSeqdst): } III:-

Как и прежде, без ключевого слова typename компилятор интерпретирует iterator как статическую переменную Seq<T>, что является синтаксической ошибкой, поскольку в этом месте должен быть указан тип.

Не стоит полагать, будто ключевое слово typename создает новое имя типа. Оно всего лишь сообщает компилятору, что уточненный идентификатор должен интерпретироваться как тип. Следующая строка объявляет переменную It как относящуюся к типу Seq<T>::iterator:

typename Seq<T>::iterator It:

Если же вы хотите создать новый тип, воспользуйтесь ключевым словом typedef: typedef typename Seq<T>::iterator It:

У 101ючевого слова typename существует и другое применение - оно может использоваться вместо ключевого слова class в списке аргументов шаблона при его определении:

: C05:UsingTypename.cpp

typename в списке аргументов шаблона.

tempiate<typename Т> class X { }:

int mainO { X<int> x: } III:-

Некоторые программисты считают, что это делает программу более наглядной.

Ключевое слово template

Итак, ключевое слово typename помогает компилятору понять в потенциально неоднозначной ситуации, что речь идет именно о типе. Аналогичные трудности возникают с лексемами, которые не являются идентификаторами, например, символами < и >, В одних случаях эти лексемы представляют знаки больше и меньше , в других ограничивают списки параметров шаблонов. В качестве примера снова воспользуемся классом bitset:

: СОБ:DotTempi ate.срр Конструкция .template #inc1ude <bitset> #inc1ude <cstddef> #inc1ude <iostream> #inc1ude <string> using namespace std:

tempiate<c1 ass charT. size t N>

basic string<charT> bitsetToString(const bitset<N>& bs) { return bs. template to string<charT. char traits<charT>.



Комитет по стандартизации С++ собирается снять это ограничение, а некоторые компиляторы уже сейчас позволяют использовать эти конструкции в обычном (нешаблонном) коде.

allocator<charT> >():

int mainO { bitset<10> bs; bs.set(l): bs.set(5):

cout bs endl: 0000100010 string s = bitsetToString<char>(bs): cout s endl: 0000100010 } III:-

Функция to string класса bitset преобразует содержимое битового поля в строку. Для поддержки разных строковых классов функция to string оформлена в виде шаблона по образцу шаблона basic string (см. главу 3). Объявление to string в bitset выглядит так:

template <class charT. class traits, class Allocator> basic string<charT. traits. Allocator> to string() const:

Наша функция bitsetToString() позволяет выбрать нужный тип строкового представления bitset. Например, для получения расширенной строки применяется следующий вызов:

wstring S = bitsetToString<wchar t>(bs):

Обратите внимание: шаблон basic string использует аргументы по умолчанию, поэтому нам не нужно повторять аргументы char traits и allocator в возвращаемом значении. К сожалению, функция bitset::to string не имеет аргументов по умолчанию, и написать bitsetToString<char>(bs) гораздо удобнее, чем каждый раз вводить полную запись:

bs.tempi ate to string<char. char traits. allocator<char> >()

Команда return в bitsetToString() содержит ключевое слово template в неожиданном месте - после оператора точка (.), примененного к объекту bs типа bitset. Дело в том, что при разборе шаблона символ < после лексемы to string будет восприниматься как операция меньше , а не как начало списка аргументов шаблона. Ключевое слово template в этом контексте сообщает компилятору, что за ним следует имя щаблона, благодаря чему символ < интерпретируется правильно. То же относится и к операторам -> и ::, применяемым к шаблонам. Данный способ разрешения неоднозначностей, как и ключевое слово typename, может использоваться только в коде щаблонов\

Вложенные шаблоны

Шаблон функции bitset::to string() является примером вложенного шаблона, то есть щаблона, объявленного в другом классе или шаблоне класса. Вложенные шаблоны позволяют использовать различные комбинации независимых аргументов шаблонов. Один из полезных примеров встречается в шаблоне класса complex стандартной библиотеки С++. Шаблон complex имеет типовой параметр для представления базового вещественного типа, предназначенного для хранения вещественной и мнимой частей комплексного числа. В следующем фрагменте кода стандартной биб-



1 ... 54 55 56 [ 57 ] 58 59 60 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика