Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 87
Текст из файла (страница 87)
Вспомогательные функции Как и в случае с шаблонами, предназначенными для комбинирования функторов. для шаблона Втпйех. полезно написать шаблоны функций, облегчающих составление выражений, с помощью которых происходит связывание параметров функтора. Определение таких шаблонов функций несколько усложняется из-за необходимости выражать тип связываемого значения. 22.8. Связывание значений 487 // 1ипссогв/Ь1пс)сопч.Ьрр $1пс1ис?е "йогмагс?рагеха. Ьрр" $1пс1иде "Еипсеограгаза.Ьрр" гешр1аге <1пс Р, // Положение связываемого параметра Гурепате РО> // Функтор, параметр // которого связывается хпййпе В1пс)ег<РО,Р,Воипс)ча1<еурепаве РипссогРагаш<РО,Р>::Туре» Ь1пй (РО сопвса йо, Сурепке Рогыагс?РагатТ <Сурепахае РипссогРагаха<РО,Р>::Туре>::Туре ча1) гесигп В1пс)ег<РО, Р, Воипс?ча1<сурепате РипсеогРагша<РО,Р>::Туре> > ( Йо, Воипс)ча1<сурепате РипссогРагаха<РО,Р>::Туре>(ча1)); ) Первый параметр шаблона не выводится, так что его значение должно точно задаваться при использовании шаблона Ь1пс)(), что проиллюстрировано в приведенном ниже прИмере.
// Еипссогв/Ь1пс?севе.срр Ьоо1 Типс(вМ::вег1пд сапега всг, доиЬ1е с?, 81оаг 8) ( вес?::соис « всг « « й « (с? < й 7 "<". ">=") « й « 'хп'/ гегигп с) < й; 1пс тпа1п() ( Ьоо1 геви1с = Ь1пс)<1>(йипс рсг(йипс), "Сотраг1пд")(1.0, 2.0) вес?::соие « "Ьоипс? Еипсг1оп гесигпес? « геви1г « 'хп'; () 1пс1и?е () 1 по 1ис?е ()1пс1ис?е () 1пс 1ис?е ()1пс1иде <вгг1пд> <1овггеша> "Еипсрсг.Ьрр" "Ь1пс?егб.Ьрр" "Ь1пс?сопч.Ьрр" Глава 22. Объекты-функции и обратные вызовы 488 Заманчиво было бы упростить шаблон Ьупс), добавив в него выводимый параметр шаблона, соответствующий связываемому значению. Таким образом удалось бы избежать громоздких выражений, подобных тем, что встречаются в приведенном выше примере. Однако при этом часто возникают сложности.
Например, в рассматриваемом примере литерал типа<)оиЬ1е (2 . 0) передается параметру близкого, но отличающегося типа й1оас. Кроме того, во многих ситуациях удобно было бы выполнять непосредственное связывание функции (которая передается в виде указателя на функцию). Получаемые в результате определения шаблонов Ьйпс)йр ( ) лишь немного сложнее определения шаблона Ьупд. Ниже приведен код, соответствующий функции с двумя параметрами. // Еипссогз/Ьйпс)йр2.Ьрр // Вспомогательная функция для связывания функции // с двумя параметрами с помощью указателя Севр1асе<1пс РЫша, Сурепаве КТ, сурепаве Р1, сурепаве Р2> Тп11пе Вупс)ег<уипсгйопРСг<КТ,Р1,Р2>, РЫша, Воипс)Ча1<еурепаве РипсеогРагав<РипссуопРГт<КТ,Р1,Р2>, Ранив >::Туре > > ЬупЖр (КТ (*йр)(Р1, Р2), Сурепаве Рогыагс)РагавТ <Сурепаве РипссогРагав<РипссйопРГг<КТ,Р1,Р2>, Рыща >::Туре >::Туре ча1) гесигп Вудс)ег<уипссйопРсг<КТ,Р1,Р2>, Рва, Воипс)Ча1 <Гурепаве Рипссограгав <Рипсг1опРГг<КТ,Р1,Р2>, Раша >:: Туре > >(йипс ргг(йр), Воине)Ча1<гурепаве РипссогРагав <Рипсейопргг<КТ,Р1,Р2>, РЫшп >:: Туре >(ча1) 489 22.9.
Операции с функторами: полная реализация 22.9. Операции с функторами: полная реализация Чтобы проиллюстрировать суммарный эффект, достигаемый с помощью рассмотренного усовершенствованного подхода к композиции функторов и связыванию значений, представим полную реализацию этих операций для функторов, число параметров в которых не превышает трех.
Обобщение на случай 10 илн большего количества параметров выполняется по аналогии и не вызывает принципиальных затруднений. Небольшое количество параметров выбрано лишь из соображений экономии места. Сначала рассмотрим пользовательскую программу. // йипсСогв/Еипссогорв.срр ()1пс1ис)е <1овггеат> ((йпс1иг)е <всгйпд> ((1пс1иде <гурефпйо> ()1пс1ийе "йипссогорв.Ьрр" Ьоо1 сотраге(вес)::вггйпд с)еЬидвсг, доиЫе ч1, й1оас ч2) ( 18 (г)еЬидвсг '= "" ) ( вМ::соис « деЬидвсг « ": " « ч1 « (ч1<ч22 '<' : '>') « ч2 « '1п'; ) гесигп ч1<ч2; чо1с) ргйпс пшае ча1ие (вгл)::вггйпд пале, с(оиЫе ча1ие) ( всй::соис «пате « ": " «ча1ие « '1п'; ) с)оиЬ1е виЬ (доиЫе а, с)оиЬ1е Ь) ( гесигп а-Ь; ) с)оиЬ1е смйсе (с)оиЪ1е а) ( гегигп 2"а; ) 1пг та1п() ( ив1пд вгб::соис; // Демонстрация композиции: Глава 22.
Обьекты-функции и обратные вызовы 490 соил « <Совровйсйоп геви1с: « соврове(йипс ргг(виЬ), йипс ргг(гмйсе)) (3.0, 7.0) « '~п'у // )(емонстрация связывания: соис « "В1пйфпд геви1с: « Ьйпййр<1>(совраге, "вайп()->совраге()")(1.02, 1.03) « '~п'у соис « >Вйпййпд оиериг: Ьйпййр<1>(рг1пе паве ча1ие, >сЬе и101васе апвмег со 11йе>) (42); // Комбинация композиции и связывания: соис « "Мйхйпд совров1ейоп апй Ьйпй1пд (Ьйпй<1>): «Ьфпй<1> (соврове(йипс ргг(виЫ, йипс ргг(гмйсе) ), 7.
О) (3. О] « '~п'у соиг « "Мйхйпд совровйсйоп апй Ьзпй1пд (Ьйпй<2>): «Ь1пй<2>(соврове(бипс ргг(виЫ,йипс рег(смйсе) ), 7.0)(3.0) « '~п' Вывод этой программы имеет такой вид: Совров1гйоп геви1С: -8 В1пййпд геви1с: вайп()->совраге(): 1.02<1.03 1 В1пййпд оиериг: ЕЬе и1г1васе апвмег Со 1зйе: 42 Мйх1пд совровзсйоп апй Ьйпййпд (Ьйпй<1>): 8 Мзхйпд совровйг1оп апй Ьйпй1пд (Ь1пй<2>): -8 Рассмотрев эту небольшую программу, можно сделать заключение, что вслользовашь операции с функторами, реализация которых описана в данном разделе, очень просто (несмотря на то, что сама реализавия оказалась нелегкой задачей). Заметим также, что связывание и композиция шаблонов органично сочетаются друг с другом.
Это достигается благодаря небольшому набору соглашений, сформулированному для функторов в разделе 22.6.) (стр. 457). Они не идут вразрез с требованиями, установленными для итераторов в стандартной библиотеке С++. Для функторов, которые не подчиняются принятым правилам, легко создать оболочку из класса-адаптера (как проиллюстрировано на примере шаблонов йипс рег () ). Кроме того, наше построение позволяет современным компиляторам избежать ненужных задержек в работе сгенерированного кода по сравнению с функторами, код которых написан вручную. Наконец, приведем содержимое файла типсеогорв. Ьрр, в котором перечислены заголовочные файлы, необходимые для компиляции предылушего примера.
22.10. Заключение 491 // бипссогв/йипсгогорв.Ьрр №Тйпдей Р~ЛЯСТОКОРЯ НРР №с)еййпе РОНСТОКОРЯ НРР // Определение йцпс ргг(), РипсгйопРсг, апд' РцпсЬТопРсгТ №1пс1цс)е "йцпсрсг.Ьрр" // Определение шаблона Сошровег<> №1пс1ис)е "сошровеб.Ьрр" // Определение вспомогательной функции сошрове() №1пс1цде "сошровесопу.Ьрр" // Определение шаблона В1пс)ег<>: // — включает файл Ьоипс)ча1.Ьрр с определением шаблонов // Воцпс)Ча1<> и ЯсаЫсВоцпс)т/а1<>; // — включает файл йогыагбрагаш.Ьрр с определением // шаблона РогыагдРагашт<>; // — включает файл йипсгограгаш.Ьрр с определением // шаблона Рипссограгат<>; // — включает файл Ыпс)еграгашв.Ьрр с определением // шаблона Вйпс)еграгашв<>; // — включает файл вйдпве1есг.Ьрр с определением // шаблона ЯфдпЯе1есСТ<> №1пс1цс)е "Ыпс)егб.Ьрр" // Определение вспомогательных функций Ь№пс)() и Ь№пЖр() №Тпс1цс)е "Ьйпйсопч.Ьрр" №1пс1цг)е "Ь№пс)йр1.Ьрр" №Тпс1цг)е "Ыпйбр2.Ьрр" №1пс1ис)е "ЫпЫрЗ .Ьрр" №епс)1й // РОИСТОКОРЯ НРР 22.10.
Заключение В библиотеке БТ1., входящей в состав стандартной библиотеки С++, широко используется концепция функторов. Например, с помощью фуикгоров настраивается поведение всех алгоритмов. Многие из зтих функторов представляют собой так называемые предикаты (рге4йсаюа). Предикаты — зто функторы или объекты-функции, возвращающие значения логического типа (т.е. такие, которые можно преобразовать к типу Ьоо1). Вообще говоря преликаты должны быль чистыми фуикгорами, иначе результаты работы алгоритмоа могут оказаться непредсказуемыми (см.
раздел 8.1.4 (18)). 492 Глава 22. Объекты-функции и обратные вызовы В состав стандартной библиотеки С++ входит несколько стандартных функторов и адаптеров для осуществления композиции. По сути, для всех распространенных унарных и бинарных операторов в библиотеке имеется объект-функция. Более подробную информацию по этому вопросу можно найти в 118) (разделы 8.2 и 8.3). Однако заметим, что в стандартной библиотеке С++ недостаточно адаптеров для того, чтобы обеспечить поддержку разнообразных композиций объектов-функций. Например, невозможно скомбинировать результаты двух унарных операций, чтобы сформулировать критерий наподобие "это и то".
В библиотеках Вооз1 содержатся дополнительные адаптеры, которые заполняют этот пробел 16). Приложение А Правило одного определения Правило одного определения (опе-ИеЯтпоп ги!е — О1И) — это краеугольный камень хорошо оформленного структурирования программ на С++. Наиболее общие следствия этого правила достаточно просты для того, чтобы их запомнить и использовать: следует определять невстраиваемые функции ровно один раз для всех файлов, а классы и встраиваемые функции — не более одною раза на единицу трансляции. При этом нужно следить за тем, чтобы все определения одного и того же обьекта были идентичны. Однако основные проблемы заключены в деталях, которые в сочетании с инстанцированием шаблона могут оказаться обескураживающими. Данное приложение поможет заинтересованному читателю получить всестороннее представление об О1Ж.
Если отдельные темы подробно описаны в той или иной главе книги, об этом обязательно упоминается в тексте приложения. А.1. Единицы трансляции На практике программы на С++ пишутся путем заполнения файлов "кодом". Однако границы, устанавливаемые файлом, не так важны в контексте 0)Ж. Вместо этого важную роль играют так называемые единицы трансляции. По сути, единица трансляции представляет собой результат препроцессорной обработки файла, переданного компилятору. Препроцессор удаляет части кода в соответствии с директивами условной компиляции (№1№, №1йс)еЕ и связанными с ними), удаляет комментарии, вставляет (рекурсивно) файлы, определяемые директивой № 1пс1ис)е, и разворачивает макросы. Следовательно, в контексте О(Ж файлы // Файл )зенисек.Ь)рр №1йс)е№ 00 ))ЕВ()0 №Йей1пе ЙеЬид(х) всг)::соис «х « '~п' №е1ве №г)ей1пе г)еЬид (х) №епс)1№ чо1с) с)еЬиа 1п1с() 494 Приложение А. Правило одного определения // Файл вуркод.оррз й1пс1пс)е лЬеас)ек.Ьррл 1пс шазп() ( ( с)еЬпд 1п1с() т с)еЬцд(лша1п() л) эквивалентны одному файлу: // Файл вуркод.оррт ттоЫ с)еЪпд 1п1с(); 1пс шайп() ( тзеЬпд з.п1С (); ) Связи между единицами трансляции устанавливаются с помощью соответствующих объявлений с внешним связыванием в двух единицах трансляции (например, двух объявлений глобальной функции стеЬпд 1пйс () ) или с помощью поиска, зависящего от аргумента, выполняемого во время инстанцирования экспортированных шаблонов.