Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 88
Текст из файла (страница 88)
Тип итератора определяет, какой вариант т еу ( ) использовать: сеир1асе <с1авв 1Сет> !п1!пе уо!и теу(1Сет 1!твт, 1Сет 1аяС, Ротиатг() ( // ) Сеир1ате <с1авв 1тет> !п1!пе ноы теу(1сет г!твс, 1сет 1авс, капсоилссевв) ( ) Заметим, что третий аргумент в теу ( ) фактически не применяется; он нужен только для правильной работы механизма перегрузки. Важно, что каждое свойство типа или алгоритма можно представить типом (бьгвает, специально определенным для данной цели). Сделав это, мы можем использовать такой тип, чтобы указать механизму перегрузки, какую зависящую от этого свойства функцию выбрать. Если использованный тип не соответствует какому-либо действительно важному свойству, такая техника выглядит несколько искусственной, но остается общей и эффективной.
к обычному массиву целых. Чтобы сделать это, для каждого из двух вызовов нужно выполнять различные действия в зависимости от типов аргумента функции теуетве(). Шаблон функции теуетяе ( ) просто выбирает реализацию на основе типа аргумента: Шаблоны ПЕИИИ>ИИВ Благодаря встраиванию разрешение делается на стадии компиляции, позтому подходяшая функция геч ( ) будет вызвана напрямую, без каких бы то ни было издержек во время выполнения. Заметьте, что механизм расширяемый, то есть новую реализацию теч ( ) можно добавить, не трогая старый код. Данный пример основан на идеях из работы Алекса Степанова (Бсерапоч,1993]. Иногда может оказаться полезной идентификация типа во время исполнения (см.
раздел 14.2.5). 15.7. Синтаксис Первоначально я хотел поместить аргумент шаблона сразу после его имени: с1аяя чесгог<с1аяя Т> // ): Но такой синтаксис не всегда мог. использоваться с шаблонами функций ~8сгопзсгпр, 1988Ь): «На первый в»тля/ь синтаксис функций выглядит логично и без нового ключевого слова: та !пс)ех<с1аяя т>(чессог т>а ч, !пс !) ( /* ... */ ) Обычно параллель с шаблонами классов не нужна, поскольку аргументы шаблонов функций, как правило, явно нв задаются: !пС ! = !пс)ех(ч1,10); с)тая* р = !пс)ех(чрс,29)г Однако иэ-за такого упрощенного синтаксиса возникают сложности. Объявление шаблона оказывается трудно найти в программе, поскольку его аргументы слишком глубоко размещены в синтаксисе функций и классов, так что разбор шаблонов функций чрезвычайно труден. Можно написать синтаксический анализатор для С++, способный обрабатывать такие обьявления шаблонов функций, в которых аргумент используется до того, как определен (см.
выше пример с функцией !пг)ех () ). Я написал такой анализатор, но это было нелегко, а техника разбора там далека от традиционной. 6<ли бы не было введено новое ключевое слово и не требовалось объявлять аргумент шаблона перед вго использованием, это привело бы к тем же сложносшм, что возникают иэ-эа чересчур запутанного синтаксиса объявлений в С и С++». В окончательном варианте синтаксис объявления функции 1пс)ех ( ) принимает такой вид: Сегэр1аяе<с1аяя Т> Та !пс)ех(чесяог<Т>а ч, !пС !) ( /* ... */ ) В то время я серьезно обдумывал вариант синтаксиса, когда возвращаемое значение функции помещается после аргументов. Например: !пс)ех<с1аяя Т>(чесяог<Т>а ч, зпС !) теСпгп Та ( /* ...
*/ ) или !пс)ех<с1аяя Т>(чесгог<Т>а ч, !пС !) : Та ( /* ... */ ) С помощью данного варианта можно было решить проблемы синтаксического разбора, но многим пользователям удобно, когда имеется отдельное ключевое 11ИИИИИИИП Синтаксис слово, помогающее распознать шаблон, так что такого рода ухищрения оказались излишними. Угловыс скобки <... > были выбраны вместо фигурных по ряду причин: во-первых, многим пользователям казалось, что они легче читаются; во-вторых, как уже отмечалось выше, фигурные скобки и так часто используются в синтаксисе С и С~-+. Тем нс менее существовала определенная проблема. В предложении Ыяс<ььяс<1пс» а; на первый взгляд, объявляется список списков целых. На самом деле, это синтаксическая ошибка, поскольку лексема» (сдвиг вправо или оператор вывода) — не то же самое, что две лексемы >. Разумеется, простой лексический трюк решил бы данную проблему, но я решил не запутывать ни грамматику, ни лексический анализатор.
Однако с тех пор эта ошибка встречалась так часто, что теперь я испытываю сильнейшее желание ликвидировать проблему. 15.8. Методы композиции Шаблоны поддерживают несколько безопасных и весьма мощных приемов композиции. Например, это средство можно применять рекурсивно: Сешр1аое<с1аяя т> с1аяя Ььво ( /* ...
*/ ); ььяс<1пс> 11; ывс< ььяс<1пс» Ш ; Ыво< Ыяо< Ььяо<ьпо» > 1111; сешр1асе<с1аяя т> с1авя ьзяс2 : ро)>11с ььяс< ььяс<т» ( ); Сешр1аое<с1аяя Т> с1аяв Ыяоз : ро)>11с Ыяо2< Ььял<т» ( ); ььяс2<ьпс> 1112," Ььяоэ<1по> ШЬЗ; Подобное использование наследования несколько необычно, поскольку не добавляется никаких новых членов. Наследование не сопряжено с дополнительными расходами времени или памяти; это всего лишь метод композиции.
Если бы композицию нельзя было осуществить с помощью наследования, то для шаблонов пришлось бы изобрести какие-то особые механизмы композиции, иначе язык оказался бы гораздо беднее. Переменные данных составных типов можно использовать точно так же, как и соответствующие определенные типы, но не наоборот: чо1<) Г() ( 111 = 1112; 11!2 = 111; ) // правильно // ошибка Причина — открытое наследование определяет отношение подтипа. Если нужны конкретные составные типы, то их легко определить с помощью наследования: Шаблоны ИИИИИИИ11' Для того чтобы разрешить присваивание в обоих направлениях, понадобилось бы расширение языка, вводящее настоящие параметризованные синонимы.
Например: севр1асе<с1аяя т> суре<)ег ывс< ь(вс<т» ъьвс4г уоЫ (ряс< ь(вс<т»а 1яс1, ь(вс4а 1ясг) ( 1яс1 -" 1всгг 1всг = 1вс1; ) Технически такое расширение реализовать несложно, но вряд ли стоит вводить еще одно средство для переименования. Также благодаря наследованию можно частично задавать аргументы шаблонов в определении нового типа: Сепр1аве<с1авв П, с1авв Ч> с1аяя Х ( /* ... '/ ); Сеар1ате<с1авв П> с1авв ХХ : рсъ11с Х<П,(пт> ( )г В обычном случае наследование от шаблона класса позволяет «подправить» базовый класс с помощью информации, нужной производному классу. Благодаря этому композиция становится более гибкой.
Например: севр1асе<с1авв т> с1аяя заве ( /* ... */ ); с1авя Пет1уег) г ря)>11с Вава<Пег(уед> ( /* ... '/ ); Описанная методика позволяет информации о производном классе попадать в определение базового класса. См. также раздел 14.2.7. 15.8.1. Представление стратегии реализации Еще одно применение наследования и шаблонов для композиции — метод передачи объектов, представляющих стратегии реализации. Например, семантику сравнения для сортировки или средства распределения и освобождения памяти для контейнера можно было бы задать с помощью аргументов шаблона 12п((): «Один иэ возможных способов — испольэовать шаблон длв составления нового клосса иэ интерфейса длл нужного контейнера и класса распределителя памяти, применяя технику раэмещения, описанную в (2пгт, $6.7.21г сеар1асе<с1аяя т, с1авв А> с1авв сопсго11ег( солса(пег ри)э11с Сопва1пет<Т>, рт(таге А ( // уотг( яопе гипсс1оп() ( // Т* р = пан(А:горетатот пеи(я(вест(Т))) Т; // ) // ЯИИИВИИИ3 Методы композиции с1авв ЗЬагей : рпЫ1с Агапа ( /* ...
*/ ); с1авв Раас а11осасог ( /* ... */ ); с1авв Регв1всепс : риЬ11с Лгепа ( /* ... */ ); Сопсго11ей сопса1пег<ргосевв йевсг1рсог,Знатей> рГЫ; сопгго11ай сопсайпег<нойе,Раве а11осагог> сгее; Сопсго11ей сопса1пег<Регвоппе1 гесогй,регв1всепс> рауго11; Это обычный способ передать нетривиальную информацию о реализации производному классу. Преимущества приема — систематичность и возможность использовать встраивание. Правда, в данном случае нередко возникают довольно длинные имена. Но для них можно ввести синонимы с помощью гурейеГл.
В компонентах Буча (Воос)), 1993] такой способ композиции используется повсеместно. 15.8.2. Представление отношений порядка Рассмотрим задачу сортировки. У нас есть шаблон контейнера„тип элемента и функция, сортирующая элементы в контейнере. Мы не можем поместить критерий сортировки внутрь контейнера, поскольку обычно хранящиеся в нем элементы не должны зависеть от него. У нас также нет возможности поместить этот критерий и внутри типа элементов, поскольку их можно сортировать разными способами.