Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 129
Текст из файла (страница 129)
<етр!а<в<с!им В1лОр> с1азз Ь!п«ег2и«г р«ЬВс илагу7«испол«урепате В)пОр: (1<гз< агаитеп« уре, <урелате ВтОр <: гезид <ура> Мы обязаны писать!езз <Вал<<и<> ( 7), а не 1езз <Ьап ( 7), потому что шаблонный аргумент «и<> не может быть выведен из типа аргумента конструктора 7(513.3.1). Предикат 1ет <Вал полезен во многих случаях. Заметим, что мы определили его, связав (зафиксировав) второй аргумент операции «меньше», Такая техника очень важна и широкоупотребительна, а стандартная библиотека предоставляет специальный класс и вспомогательную функцию для выполнения этой задачи: Глава ) 8.
Алгоритмы и классы функциональных объектов 622 ( рго!ес!ей: ВглОр ор; !урепате ВгпОр::зесолй агвитеп! !уре агл2! риЫ(с: Ыпйег2пй(сопя! В!пОря х, соля! !урепагае ВМОр::зесопй оголтел! !урез г) : ор(х), агд2(г) () гезии !уреорегагог() (соля! аглитеп! (урез х) солт (гевал ор (х,агл2) ! ) ): !етр1а!е<сдтя В1пОр, едят Т> Ьгпйег2пй<В(пОр> Ьгпй2пй (соля! ВшОрь ор, соля! Ть «) ( гетгп Ьгпйег2лй<ВгпОр> (ор, г) ! Например, мы можем применить Ь1пй2пй() для создания унарного предиката «меньше чем 7» из бинарного предиката «меньше» и значения 7. гогй 1(дя!<!и!> ь с) ( 1Ь!<!пя>::солт Вега!огр =ЯпйЯ(с.Ьея!п(),с.елй(),Ыпй2пй(1еяя<ш!> (), 7) ) ( 77 .
Насколько это читаемо и эффективно? Даже в средне~о качества реализации С++ это намного эффективнее по быстродействию и расходу памяти (легко допускает встраивание), чем версия с функцией 1езз ЬЬап 7() из 818.4. Эта система обозначений довольно логична, но к ней нужно привыкнуть. Часто бывает полезно определить явно именованную операцию со связанным аргументом; гетр!а<е<с(аяз Т> з!гис! 1езз !Ьап: риЫ(с Ыпйег2лй<1езз<Т» ( ехрдсй 1езз сдал (солт Ть х): Ыпйег2пй<1езз<Т» (!езз<Т> (),х) ( ) )! го1й1(1В!<!и!>ь с) ( !!яг<ш!>::сопзг Вега!огр =1)лй Ц(с.веа!и (),с.епй(),!езз !дал<!п!> (7) ) У ...
) Важно определять 1еяя гйап именно через 1езз, а не через операцию <. При этом 1езя !Ьал воспользуется всеми преимуществами любой возможной специализации 1езз (813.5, 819.2.2). Параллельно с Ьйпй2лй() и Ь!пйег2пй, в файле <уипспопа!> определяются еще и Ыпй1зг() и Ь!пйег1ждля связывания первого аргумента бинарных функций. 18.4. Классы функциональных объектов 623 18.4.4.2. Адаптирование функций-членов Большинство алгоритмов использует в своей работе стандартные или определенные пользователем операции. Естественно, возникает необходимость и в использовании функций-членов. Например (83.8.5): ю!Из!гав аИ(!Ь«Яворе*>ь е) ( !ог еась (с.Ьеа!п (), с.епб(), ьбааре::Исав) ! ) У еггог Но проблема заключается в том, что функция-член тУ( ) должна вызываться от имени объекта: р->т1(), в то время как алгоритмы, например !Ьг еасй (), вызывают свои аргументы-функции обычным способом: у() .
Следовательно, мы нуждаемся в механизме создания чего-то такого, что позволило бы алгоритмам просто и аффективно вызывать функции-члены. Альтернативой было бы дублирование алгоритмов: по одной версии для функций-членов и по другой версии для обычных функций. И даже хуже того: потребовались бы разные версии для контейнеров объектов и для контейнеров указателей на объекты. Как и в ранее рассмотренном случае связываюших адаптеров (818.4.4.1) проблема решается введением класса плюс вспомогательной функции.
Рассмотрим сначала типичный случай, когда нужно вызвать функцию-член без аргумента для контейнеров указателей на объекты: гетр(иге<с(азз Я, е!азз Т> с(азз тет Тип 1: риЫ!с ипагу гипепоп<Т*, М> ( И (Т::"рт)) (); риЫзс: ехрдс1гтет !ип З(И (Т::"р) () ): рт!(р) () И орегазог() (Т* р) сопи (ге!игл (р->*рт!) (); ) И вызов по указателю ): (етр!азе<е(азз Я, с(азз Т> тет уип з<И, Т> тет Гип (М (Т: с к!) () ) ( ге!ига тет Тип з<И, Т> (!) ! ) Это позволяет нам исправить пример с ойаре:: в(гам (); юЫ дгаы аИ (1ЬЗ<бааре*>ь Ьр) У вызываем безаргументную функцию-член И через указатель на объект ( /ог еасЬ (Ьр.
Ьеагп (), Ьр. епб ( ), тет)ип ( вЯюаре:: Игаы) ) 1 И рисуем все фигуры гетр!а1е<с!ат Я, с(азз Т> тет 1ип з<И, Т> тет уип (И (Т:: "з) () ) ) И а такзюе версии длн унарных, константных и укорных константных функций-членов У (см. таблицу в,~!В.4.4) Теперь перейдем к функциям-членам, имеюшим аргументы, для чего потребуется класс и соответствуюшая функция тепз )ззп().
Кроме того, потребуется еше и версия для работы от имени объектов (а не указателей) — ее называют тепзрзп геу() . Наконец, нам нужны версии для константных функций-членов: 624 Глава ) 8. Алгоритмы и классы функциональных объектов гетр(аге<с(аев М, с1ат Т> тет /ип ге1 1<К, Т> тет)ил ге)(К (Т:: »1) () ); // а также версии длн укорных, константных и унарных константных функций-членов // !си. таблицу в ~!В.4,4) С этими адаптерами функций-членов, определенными в заголовочном файле <Влпспопа1>, мы можем писать следующий клиентский код; гоЫ 1(1!зг<згг/л8> ь Ь) //используем длн объекта безаргументную функцию-член ( (урейеТ Взг<згпнд>:: !1егагог 1 »1! ЕЯр = 1!лй зТ(Ь.Ьеа!л(),Ь.епй(),тет /ин геГ(ьзгг/ля: зетр(у) ); //находим ) гоЫ гога1е аП(1!з1<ЯЬаре*>а Ь, зп! ап81е) // используем функцию-член, принимающую аргумент через указатель на обьект ( /ог еасЬ (Ь.
Ьея/л (), 1з. елй(), Ьзпй2пй(тет 1ип (йбйарез з го!иге), ал81е) ); ) В стандартной библиотеке нет необходимости адаптировать функции-члены с более чем одним аргументом, так как нет алгоритмов, принимающих функцию с более чем двумя аргументами. (етр!иге<с!авв А, с!ат Я> ро!пгег (о ипагу 1ипсйол<А,М> ргг 1ип (Я («1) (А) ); (етр(осе<с(азз А, с)азз А2, с!от Я> розп1ег го Ьтагууипсг!он<А,А2,Я> ргг /ип (Ю (»1) (А, А2) ) С этими адаптерами указателей на функции мы можем применить «связывающие» адаптеры следующим образом: сйззз Несогй ( /* ...
*/ ); Ьоо! пате Ьеу ер(сопл( Яесоп1ь, сопл! сваг*); Ьоо! ззп Ьеу ер (соплйесогйь, 1опд) / Дери«некие на базе имен //сравнение на базе чисел юЫ 1(1Ьг<Юесогй> ь 1г) ( (урейе1 Ьзг< Йесоп1>:: Вега!ос Й1 ! // используе»з указатель на функцию 11 р = Впй (Т (1г. бездн ( ), 1г. епй ( ), Ьтй2нй (рзг /ил (пате Ьеу ер ), "Уолл Вгозгп" ) ); 11 р = Впй ~~(1г.
Ьедт ( ), Ь.. елй ( ), Ьии12пй (ра /ип (звп lщ ер ), 12345б 7890) ); // ... ) Тут мы ишем элементы списка 1г, которые соответствуют ключам поиска «Хойл Вголп» и 1234547890. 18.4.4.3. Версии адаптеров для указателей на функции Алгоритмам не нужно знать, является ли их аргумент-функция просто функцией, указателем на функцию или функциональным объектом.
А вот «связывающим» адаптерам (518.4.4.1) это знать нужно, так как они сохраняют копию для последующего применения. Поэтому в заголовочном файле <Визсйопа1> имеются еше и версии адаптеров для указателей на функции, которые можно использовать со стандартными алгоритмами. Их определение и реализация весьма близки определению адаптеров функций-членов (818.4.4.2) — снова используются пара функций и пара классов: 625 18.4. Классы функциональных объектов 18.4.4.4. «Отрицатели» Предикатные «отрицатели»' похожи на «связываюшие» адаптеры тем, что принимают операцию и порождают на ее основе необходимую операцию. Определение и реализация «отрицателей» весьма близки определению адаптеров функций-членов (518.4.4.2).
В целом они тривиальны, но окончательной простоте мешают слишком длинные стандартные имена: 1«тр1азе< с(ат Ргей> с1азз ипагу пеяаге: риЫ!с илагу 1ипс11оп<гурепате Ргей:: аглителг (уре, Ьоо1>' ( Ргей ор; риЫ(с: схр11«11 ипагу лелаге(солИ Ргейа р): ор(р) ( ) Ьоо( орега1ог() (сопз1 аглитеп1 (урез х) сопи (ге!игл ! ор (х) 1 ) )! гетр(ага<с!ат Ргей> с(азз Ь(лагу пеяазе: риЬПс Ыпагу)ипсг/оп~урепате Ргей::1тз1 аглитепг гуре, гурепате Ргей::зесопй агяитеп1 гуре, Ьоо1> ( сурой«уу)гз1 аглитет гуре Агя; зурейс/ зесопй агяитеп1 гуре Аг821 Ргей орг риЫ(с: ехрдсй Ыпагу пела1е (сот! Ргейь р): ор (р) ( ) Ьоо1 орегагог Д (сопзг Агаа х, сопзг А«82з у) сот! (ге!игл ! ор (х,у) ! ) )! гетр(ага<с(азз Ргей> ипагу пеяазе<Ргей> по!1 (сопзг Ргейь р) ! гетр(аге<с1азз Ргей> Ь(лагу пела(е<ргей> по!2 (сопи Ргейа р); Эти классы и функции объявлены в заголовочном файле <!илспола1>, Имена узгзг агяителт гуре, зесопй агяителг гуре и т.д.
берутся из стандартных базовых классов илагууилспол и Ьшагу уипспол. Как и «связывающие адаптеры», «отрицатели» удобно применять с помощью их собственных вспомогательных функций. Например, мы можем построить бинарный предикат «не меньше чем» и применить его для поиска самой первой пары элементов, у которой первый элемент меньше второго элемента: го/йу'(гесгог<1л1>а и, 1из<тг> З П) // пересмотреииый пример из 818.4 2 ( // ... р1 = тйтагсЬ ( и.
Ьед/п ( ), и. епй ( ), П. Ьеягп ( ), по!2 (!езз <!па> ( ) ) ); // ... Здесь р1 идентифицирует первую пару элементов, для которой предикат «не меньше чем» не выполняется. То есть, адаптеры, придающие противоположный смысл предикату. — Прим, ред. 18. Алгоритмы н классы функциональных объектов 626 Предикаты работают с логическими условиями, и поэтому здесь нет эквивалентов побитовых операций ), а, " и -. Естественно, разные типы адаптеров можно сочетать друг с другом. Например: екгегп»О» 1п! застр (сопя! слог*, сопя! сьаг*); !! из <ся!Мпя> гг' используется указатель на функцию гоЫ Г(1(я!<сваг*>а 1я) гуре«(е! (урепате !61<слог» >:: сонм 1!егагог 1.1; 11р= !!т! (Г(1я.Ьее(п (),1я.епа(),по!1 (Ь(па2пИ(р!г уип (япстр), "1иппу») ) ) ) ) Здесь находится элемент списка 1я, содержащий С-строку "уиппу", «Отрицатель» нужен из-за того, что яягсяпр() возвращает нуль при равенстве строк.