Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 11
Текст из файла (страница 11)
5.1. Ключевое слово 1уреваше Это ключевое слово введено в язык в процессе стандартизации С++ для указания того, что идентификатор в шаблоне является типом. Рассмотрим следующий пример: Селюр1аее <куренное Т> с1авв МуС1анн ( Сурепщае Т::яиЬТуре * рпг; Б этом примере второе ключевое слово сурепате используется для пояснения, что ЯпЬТуре является типом, определенным внутри класса Т. Таким образом„рек является указателем на Т:: ЯпЬТуре. Без такого указания с помощью Сурепщве идентификатср яиЬТуре интерпретиро валех бы как статический член класса, т.е.
как конкретная переменная или объект. В результате выражение Т::ЯиЬТуре * рог представляло бы собой умножение статического члена класса яиЬТуре на ркг. Б общем случае ключевое слово сурепщае следует использовать всякий раз, когда "мя, зависящее от параметра шаблона, представляет собой тип (более подробно этот вопрос рассматривается в разделе 9.3.2, стр. 154). Глава 5. Основы работы с шаблонамн бб Типичным применением сурепке является доступ к итераторам контейнеров БТЬ в коде шаблона. // Ьавйсв/ргйпссо11.Ьрр $1пс1цг)е <Ьовсгеазл> // Вывод элементов контейнера ЯТЬ чоЫ ргйпссо11 (Т сопвей со11) ( сурепазае Т::сопит Ьгегагог ров; // Итератор для // цикла по со11 сурепке Т::сопзг 1сегасог епс)(со11.епс)()); //Конечная позиция Хог(ров = со11.Ьедйп(); ров )= епд; ++ров) ( зМ::сонг « *роз « ) всей::соиг « *роз « еп61; ) В этом шаблоне функции параметр вызова является контейнером БТЬ типа Т.
Для цикла по всем элементам контейнера используется итератор, который объявлен внутри каждого класса контейнера как тил сопзг 1гегасог. с1авв зс2сопсайпег ( суре<)ей ... 1сегасог // Итератор для доступа // для чтения/записи Гурег)ей ... сопев йсегасог; // Итератор для доступа // только для чтения Таким образом, для того чтобы получить доступ к типу сопит Ьгегагог шаблона типа Т, нужно выполнить уточнение типа с использованием ключевого слова Гурепаше: Гурепазае Т::сопев Тсегасог ров; Конструкция .Ьешр1аЬе Очень похожая проблема была обнаружена в С++ и после введения в язык ключевого слова сурепшле.
Рассмотрим пример, в котором используется стандартный тип Ь1 се ос. Гешр1асе<йпс Н> чоЫ ргйпСВЬГвес(вМ::Ьйсвес<Н> сопвсй Ьв) ( всб::сонг « Ьв.геазр1аге го всгйпд<сЬаг, сЬаг ггагв<сЬаг>, а11осагог<сЬаг»() з 5.2. Использование (Ыз-> 67 В этом примере присутствует непривычная конструкция — . севр1асе. Зачем она нужна? Если не использовать здесь "лишнее" ключевое слово севр1асе, то компилятор не будет знать, что знак "<" на самом деле означает не "меньше чем", а начало списка аргументов шаблона.
Заметим, что такая проблема возникает только в случаях, когда конструкция, предшествующая точке, зависит от параметра шаблона. В нашем примере параметр Ьв зависит от параметра шаблона ы. В заключение отметим, что запись . севр1асе (и аналогичные, наподобие >севр1асе) должны использоваться только внутри шаблонов и только в том случае, если они следуют за выражением, которое зависит от параметра шаблона. Более подробно этот вопрос рассматривается в разделе 9.3.3, стр. 156. 5.2. Использование йпя-> Для шаблонов классов.
имеющих базовые классы. использование имени х не всегла эквивалентно с)з1в->х, даже если член х является наследуемым. Севр1асе <Сурепаве Т> с1анв Ване риЫ1с: нойс( ехйс(); севр1апе <Сурецаве Т> с1авв Пегфчес): Ваве<Т> ( риЫ1с: Тс) Юоо() ( ехйс(]; // Вызов внешней функции ех1С() // или ошибка В этом примере при разрешения имени ехзс ( ) в теле боо ( ) никогда не рассматривается функция ехйс ( ) из класса Ване. следовательно, либо будет выведено сообщение об ошибке, либо будет вызвана другая функция ех1с () (например, стандартная фУнкция С ехзс ( ) ). Более подробно этот вопрос рассматривается в разделе 9.4.2, стр.
161. А пока рекоменлуем использовать следующее правило: всегда необходимо полностью указывать любое имя, обьявленное в базовом классе, который каким-либо образом зависит от паРаметра шаблона. для этого можно использовать конструкции г )тзв -> или Ваве<т>:: . Чтобы гарантированно исключить какую бы то ни было неопределенность, можно использовать полное имя при любом обращении к членам классов (в шаблонах). Глава 5.
Основы работы с шаблонами 68 5.3. Шаблоны-члены классов Члены классов тоже могут быть шаблонами; это справедливо как для-вложенных классов, так и для функций-членов. Применение и преимущества такой возмЬжности можно еще раз продемонстрировать на примере шаблона класса Якас)с<>. Обычно стеки можно присваивать друг другу только в том случае, если они имеют одинаковый тип, что предполагает одинаковый тип их элементов. Однако стеку невозможно присвоить стек с элементами любого другого типа, даже если для типов элементов определено неявное преобразование типов. ЯеасМ<1пс> 1псЯСао)<1, 1псясас)<2; // Стеки для // целочисленных значений // Стек для значений // с плавающей точкой Ясас)с<й1оас> Е1оасЯеас)с 1пСЯСасК1 = ТпСЯСас)<2) Й1оасЯсас)с = ТпеЯсас)<1; // КОРРЕКТНО: стеки имеют // одинаковый тип // ОШИБКА: стеки имеют // разные типы Используемый по умолчанию оператор присвоения требует, чтобы с обеих сторон оператора использовался один и тот же тип, но если типы элементов у стеков различны, то это не так.
Однако если задать оператор присвоения в виде шаблона, то присвоение стеков с элементами, для которых определено соответствующее преобразование типов, станет возможным. Для этого необходимо обьявить Ясас)с<>, как показано ниже. // Ьаз1сз/зсас)<5с)ес1.Ьрр // Элементы рцЬ11с: чойс) рцзЬ(Т сопвса); чо1с) рор(); Т Сор() сопзе; Ьоо1 ешрсу() сопзс ( кесшп е1елш.ещрсу(); // Присвоение стека элементов с типом Т2 сЕщр1аСЕ <СурЕПаШЕ Т2> Яеао)С<Т>а ОрЕГасск= (Ясао)«Т2> СОПЗса)/ Сешр1аке <Сурепаше Т> с1авв Ясас)с ( рг1часе: зМ:: с(ес(це<Т> е1елш; // )(обавление элемента // Снятие элемента // Возвращение элемента // с вершины стека // Возвращается Скце, // если стек пуст 5 З, Шаблоны-члены классов 69 Были сделаны два изменения.
Добавлено объявление оператора присвоения для стека с элементами другого типа т2. 2 Теперь в качестве внутреннего контейнера для элементов стека используется очередь с двусторонним доступом. Это следствие реализации нового оператора присвоения. реализапия нового оператора присвоения показана ниже. // Ьав1св/всасК5аввйцп. Ьрр Севр1ате <Сурепаве Т> Севр1ате <сурепаве Т2> ятаск<т>й ясасК<т>::орегасог = (ясасК<т2> сопвсй ор2) ( Н ((чоЫ*)сЬ?в == (чей*)йор2) ( // Присвоение // самому себе? теспгп *сЬйв; ятасК<Т2> Свр(ор2); // Создание копии // присваиваемого стека е1евв. с1еаг(); // Удаление существующих // элементов мЬ11е(!твр.еврту()) ( // Копирование всех элементов е1евв.рпвЬ йгопг(твр.тор()); гвр.рор()г тегптп *тЬйв; Прежде всего посмотрим на синтаксис определения шаблона-члена. Внугри шаблона с параметром Т определяется внугренний шаблон с параметром т2.
севр1аге <Гурепаве Т> Гевр1ате <Гурепаве Т2> казалось бы, в теле функции-члена можно просто обращаться ко всем необходимым данным присваиваемого стека ор2. Однако этот стек имеет другой тип (при инсганцировании шаблона класса для двух разных типов данных будут получены стеки двух разных типов), поэтому использовать открытый интерфейс здесь нельзя.
Отсюда следует, что единственный способ обращения к элементам стека — это вызов гор ( ) . Однако для этого кажлый элемент должен находиться в вершине стека Таким образом, сначала нужно сделать копию оР2 с тем, чтобы можно было удалять элементы при помоши вызовов рор ( ) . Поскольку функция тор ( ) возвращает последний элемент, помещенный в стек, необходимо использовать контейнер, который поддерживает вставку элементов в противоположный конец коллекции. Поэтому используется очередь с двусторонним доступом, у которой имеется функция рпвЬ йтопс ( ), помешаюшая элемент в начало коллекции. Глава 5.