Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 83
Текст из файла (страница 83)
Введение Независимые концепции должны представляться независимо и комбинироваться лишь при необходимости. Когда этот принцип нарушается, вы либо связываете воедино разнородные концепции, либо создаете ненужнгяе зависимости. В любом случае вы порождаете негибкие конструкции для построения программных систем. Шаблоны обеспечивают простой способ представления широкого набора общих концепций и простые способы их комбинирования. Получающиеся результирующие классы и функции не уступают ручным специализированным вариантам по эффективности и потребляемой памяти. Шаблоны обеспечивают непосредственную поддержку обобзценного программирования Гбепепсргоегаттзпя) 82.7), то есть программирования с использованием типов в качестве параметров.
Механизм шаблонов языка С++ позволяет использовать типы в качестве параметров определений классов или функций. Шаблон зависит лишь от тех свойств параметров-типов, которые он использует явно, и не требует никаких дополнительных зависимостей между ними. В частности, не требуется, чтобы разные параметры-типы шаблона принадлежали к одной иерархии наследования. В настоящей главе изучение шаблонов фокусируется на тех вопросах, которые заложены в дизайн и реализацию стандартной библиотеки, а также на вопросах ее 402 Глава 13.
Шаблоны практического использования. Стандартная библиотека требует существенно большей степени общности, гибкости и эффективности, чем большинство иных программных систем. Следовательно, использованные в ней технологии действенны и эффективны при разработке программных решений для широкого круга задач и предметных областей. Эти технологии позволяют разработчику скрывать сложные технические детали за простыми интерфейсами и открывать их пользователю лишь тогда, когда в них возникает действительная нужда.
Например, через функциональный интерфейс воп(ч) открывается доступ ко множеству алгоритмов, сортирующих наборы элементов разного типа, содержащихся в самых разных контейнерах. Наиболее подходящая для конкретного ч функция сортировки будет выбрана автоматически. Любая существенная абстракция стандартной библиотеки представлена шаблоном (например, хп!ия, озггеат, сотр1ех, 1Ы и я»ар), равно как и фундаментальные операции над ними (сравнение строк, операция вывода «, сложение комплексных чисел, получение следующего элемента списка, сортировка). Специально посвященная стандартной библиотеке часть |П настоящей книги является обильным источником по «настоящим» шаблонам и практическим методам работы с ними.
А текущая глава опирается на небольшие примеры, которые иллюстрируют следующие технические аспекты шаблонов и фундаментальные способы их использования: ° 513.2: Основные механизмы определения шаблонов классов и их использование. ° 8!3.3: Шаблоны функций, перегрузка и логический вывод типа. ° 513.4: Параметры шаблонов, управляющие поведением обобщенных алгоритмов.
° 513.5: Множественные определения, обеспечивающие альтернативные реализации шаблонов. ° 513.6: Наследование и шаблоны (полиморфизм на стадии выполнения и полиморфизм на стадии компиляции). ° 513.7: Организация исходного кода. Начальное знакомство с шаблонами состоялось в 82.7.1 и 83.8. Детальные правила разрешения имен шаблонов, синтаксис шаблонов и т.д, представлены в 5С.13. 13.2.
Простой шаблон строк Рассмотрим строку символов. Строка является классом, хранящим символы и обеспечивающим доступ по индексу, конкатенацию, сравнение и другие операции, которые мы обычно ассоциируем с понятием «строка». Такое поведение желательно реализовать для разных наборов символов, например, европейского набора символов, набора китайских символов, греческих и т.д. Поэтому целесообразно определить суть понятия «строка» в отрыве от специфических символьных наборов. Это определение базируется в основном на идее о том, что символы можно копировать. Таким образом, общий строковый тип можно создать из строк символов типа сваг (511.12), сделав тип символов параметром; 403 13.2. Простой шаблон строк гетр!иге<с(алл С> с(авв Я!ггпу ( л!гис! Ягер( .>гер* гер; риЫ1с: 5(ггпя() ' .с!кгпв (сопл! С* ) ' Я! 1пе (сопл! о!г(пей ); С геев(иг! г'] сопл!; гг' ...
Префикс !етр1а!е<с1алл С> указывает, что объявляется шаблон, и что идентификатор С является параметром типа. В теле определения шаблона С используется точно так же, как и другие имена типов. Область видимости С простирается до конца определения шаблона. Обратите внимание на то, что !етр!а!е<с1шв С> означает, что С вЂ” это любой тип, не обязательно класс.
В соответствии с определением шаблона его имя с последуюшим конкретным типом, заключенным в угловые скобки, является именем класса и его можно использовать так же, как имя любого другого класса. Например: Яг(ад<сваг> сл; Я!ггпу<пил(дпед сваг> ил; Ятпд<нсваг г> нл; с(аев .!сваг ( д японские символы )' огг1пд<Хсаа«> р; За исключением специального синтаксиса имени, Ягг1щ<сйаг> ведет себя так же, как и определенный в 511.12 класс Я г1пд. Преобразовав ЮМп8 в шаблон, мы получили возможность использовать средства, предназначенные для работы со строками символов типа сйаг, в работе со строками символов иных типов. Например, применив строковый шаблон и стандартный библиотечный контейнер тар, мы превратим наш пример с подсчетом слов из 511.8 в следуюший код; //подсчет кол-ва вхождений каждого слова Ы! та(п () Я! 1пя<сааг> Ьи!г тар<8!егия<сваг>, Ыг> т; ыЫ!е (с1п»ЬиЯ т(ЬиЯ эч; гу вывод релулыпота Глава 13.
Шаблоны 404 А вариант для японских символов типа Хсйаг будет выглядеть так: У подсчет кол-ва вхождений каждого слова гп! таиг() 1 ,Угг(пя<сьаг> Ьиу! вар<Я<с!пд<усдаг>, !пг> ги ! тЬ!!е)с!п»Ьи!) т(ЬиЯ -';-ь! гз' вывод результата В стандартной библиотеке определяется шаблонный класс Ьал!с лег!пд, аналогичный шаблонному варианту агг!пд (511.!2, 520.3). А вгг!пд определяется в стандартной библиотеке как синоним для Ьш(с вгг(ад<айаг>: гурейеу Ьав(с в!с!па<сдпг> в!с!ад; Это позволяет записать программу подсчета слов в следующем виде: оподсчет кол-ва вхождений каждого слова !п! тат() вгг!па Ьи(! тор<в!с!ив, !пг> т; тЬ!!е(сгп»Ьи!) т! Ьи() +е; зУ вывод резулыпата Вообще, брег(еу часто применяют для сокращения длинных имен классов, сгенерированных из шаблонов. Поскольку мы часто предпочитаем не знать всех деталей определения типа, орЫеу'помогает скрыть тот факт, что тип сгенерирован из шаблона.
13.2.1. Определение шаблона Класс, генерируемый из классового шаблона, является абсолютно нормальным классом. Применение шаблонов не требует никаких дополнительных механизмов поддержки времени выполнения по сравнению с применением вручную запрограммированных классов. Пе предполагает механизм шаблонов и какого-либо уменьшения объема сгенерированного кода. Обычно хорошей идеей является тестирование и отладка конкретного класса, например агг!пд, до его преобразования в шаблон (в нашем примере в Ягг!ад< С>).
Таким образом удается разрешить многие проблемы проектирования и выловить ошибки реализации, оставаясь в рамках конкретного класса. Такой способ отладки знаком всем программистам, а многим из них легче работать с конкретным примером, чем с абстрактной концепцией. Позднее, мы можем сосредоточиться на проблемах, связанных с обобщением, не отвлекаясь при этом на борьбу с обычными ошибками. Аналогично, разбираясь с шаблоном, бывает полезным сначала представить себе его поведение для конкретного типа вместо произвольного типа-параметра, и лишь потом пробовать осознать шаблон во всей его общности. Члены шаблона класса объявляются и определяются так же, как и в случае обычных классов.
Функцию-член шаблона класса не обязательно определять внут- 13.2. Простой шаблон строк 405 в!гис! Ю!г!ля< С>:: Югер !етр1а!е<с1авв С> ( С* в; !п! вс! !и! и; У... )' г указатель но элементы ~У число элементов У подсчет ссылок гетр!иге<с!иве С> С Юге!па<С>:: геев(1п! 1) сопке (ге!ига гер->в [!]; ) !етр!а!е<с(аев С> Юге!ля<С>::Ю!г(пя () ( гер = пет Югер (О, С () ); Параметры шаблонов, такие как С, являются именно параметрами, а не именами типов, определенными внешним образом по отношению к шаблону. Это, однако, никоим образом не влияет на способ, каким мы его используем при написании кода шаблона.
В области видимости Ю)г1пя<С> квалификация с <С> избыточна для имени шаблона, так что Юге[па<С>:: Юптпя есть имя конструктора. Но если уж мы хотим предельно явных формулировок, то можно писать и так: !етрга!е<с!авв С> Югг!па< С>:: Юи 1пя<С> ( Мер = пея Югер (О, С() ); ) Аналогично тому, как для обычного класса может существовать лишь одно определение для его функции-члена, также может существовать лишь один шаблон функции в качестве определения функции-члена классового шаблона.
Перегрузка возможно только для шаблонов-функций (513.3.2), в то время как специализация ($13.5) позволяет обеспечить альтернативные реализации шаблонов. Перегрузка имени шаблона класса невозможна, поэтому в области видимости шаблона никакая другая сущность с тем же именем объявлена быть не может (см. также з)3.5). Например: гетр!аге<с1авв Т> с!иве Ю!ггпя (/* ... */); с1авв Ю!г!пя( /* ... *! ) г,(г еггог: двойное определение Тип, используемый в качестве аргумента шаблона, должен обеспечивать интерфейс, ожидаемый шаблоном. Например, тип, используемый в шаблоне Югг!пя, должен предоставлять обычные операции копирования (з10,4.4.1, 520.2.1).
Еше раз подчеркнем, что разные типы, используемые в качестве аргументов шаблона, не обязаны находиться между собой в отношениях наследования. ри самого шаблона. Ее можно определить где-нибудь еше, как это имеет место и для функций-членов нешаблонных классов (ЗС.!3.7). Члены шаблонных классов сами являются шаблонами, параметризуемыми с помощью параметров шаблона класса. Когда функции-члены определяются вне тела определения шаблона, они должны явным образом указываться как шаблоны. Например: Глава ] 3. Шаблоны Я06 13.2.2.