Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 128
Текст из файла (страница 128)
б17 18.4. Классы функциональных объектов <етр1а<е<с1азз Ага, с1азз Йез> я<гас< ипагу ~иле<!оп ( <урейе~А<й а<хите«< <уре; <уредеГЙез гсзи1« урс< <стр1а<е<с<азз Ага, с<аз< А<82, с1азз Йсз> з<гис< Ыпагу 1ипсйои ( <уреде~А<ЙЯгз< агхитеп< <уре; <уреде~А<82 засола агйитсп< <уре< <урез<ау Йез гсзиИ <уре; Предназначение этих классов — дать стандартные имена типам аргументов и возвращаемых значений для использования в классах, производных от и«агу !)<испол и Ыиагу ~и«сиоп.
Применяя эти базовые классы так, как это делает стандартная библиотека, программист избавит себя от необходимости осознания всех тонкостей, которые делают эти классы полезными (818.4.4.1). 18.4.2. Предикаты Предикат является функциональным объектом или просто функцией, имеющим возврат типа Ьооб Например, в файле <~ипсйола1> определяются: <етр1а<е<с1азз Т> <<гас< 1о8<са< ло<: риЫ(с илагу ~ипс<1оп<Т, Ьоо<> ( Ьоо< орега<ог() (сопя« Тз х) сопи (ге<игл !х< ) )< <етр1а<е<с1аи Т> з<гис< <ет. риЫ<с Ь<пагу !иле<(оп< Т, Т, Ьоо!> ( Ьоо< орега<ог () (сопя< Тя х, сопя« Ть у) солт ( ге<игл к<у< ) ): Унарные и бинарные предикаты с успехом используются в комбинации с алгоритмами.
Например, мы можем сравнить две последовательности с целью найти первый элемент одной из них, который не меньше соответствующего элемента второй последовательности: го(<1 Т(ге<<о<<1«<>ь г1, 18«тюя!1) ( <уря«еу <а«!и<>:: нега<ог 1.1< <уредсу" ге<<о<<!п<>::1<его<о< РТ< ра!г< у1, 11> р1 = тата <сЬ ( г1 . Ьел<п ( ), г1.
елд ( ), И. Ьел!л ( ), <сиз<!и<> ( ) ) Р ... ) Алгоритм т(зта<сй () многократно применяет свой бинарный предикат к парам соответствующих элементов, пока не встретится нарушение условия (818.5.4). Затем он возвращает итераторы на элементы, не удовлетворяющие поставленному условию.
Поскольку здесь нужен объект, а не тил, используется 1езз<шь () (с круглыми скобками), а вовсе не 1еяя<1«г>. б18 Вместо нахождения первого элемента, который «не меньше» соответствуюшего ему элемента другой последовательности, мы могли бы отыскивать элемент, который удовлетворяет условию «меньше».
Для этого либо используем комплиментарный предикат 8геа!ег ег!ла1: р1 = т!зтагсл (»!.Ьех!л (), г!.епг!(),!1.Ьеа!и (),егеагег еаиа1<!пг> () ) ) либо меняем местами последовательности и применяем предикат 1езз едла1: розг<2!, И> р2 = т!зтагсл (И.Ье8!и (),Й.епг!(), и'.Ьех(п (), (езз ег(иа!<!пг> () ); В 518.4.4.4 я покажу, как реапизовать предикат «не меньше», 18.4.2.1. Обзор предикатов В заголовочном файле <Я»лепила!> стандартная библиотека определяет несколько обшеупотребительных предикатов: ~ еоиа1 го ' лог еоиа1 го ' Вгеагег !езз ~ егеагег еаиа! 1 агд1<=агд2 . 'Вгпагу ,'~ 1езз еоиа! 1 ~ агд1гкгзагд2 ) Вгпагу 1 1ое!са! ап«( ' 1оз!са! ог 1ое(са1 лог ! агд1((агд2 ( Вгпагу 1 Опагу ~ (агд Определения предикатов!езз и 1оя!са1 ло! представлены в 518.4.2.
В дополнение к стандартным библиотечным предикатам пользователь может писать свои собственные. Пользовательские предикаты позволяют просто и изящно использовать контейнеры и алгоритмы стандартной библиотеки. В частности, пользовательские предикаты нужны для того, чтобы применить стандартные алгоритмы для нестандартных пользовательских классов. Например, рассмотрим вариант класса С!лЬ из 810,4.61 с!от Регзол ( г'* ... *г' ) г сагисг С1иЬ ( згг!пе пате; Бзг<Регзол*> тетЬегзг Бзг<регзоп*> оДкегзг У„. С!иЬ (солса зи!пка л) г Глава 18.
Алгоритмы и классы функциональных объектов 18.4. Классы функциональных объектов 619 Поиск конкретного клуба с заданным именем (значение поля пате) в списке !!зт<С!иЬ> задача вполне осмысленная. К сожалению, стандартный алгоритм Япй КО ничего не знает о пользовательском типе С1иЬ. Библиотечные алгоритмы умеют выполнять тест на эквивалентность, но нам не нужно сравнивать клубы по всем статьям. Мы просто хотим использовать С!иЬ:: пате в качестве ключа поиска. Для этого мы напишем следующий предикат: с!ат С!иЬ егг: риЫ!с плачу (ипс!!оп<С1иЬ, Ьоо1> ( з!г!па и риЫ!с: ехр1!с!! С1иЬ ее!(солт з!г!паз и): з (и) Ьоо! орега!ог() (солт С(иВа с) соли( (гетгп с.
пате==з! ) Определять полезные предикаты несложно. После того как мы определим предикаты для своих собственных типов, использовать стандартные алгоритмы становится столь же просто и эффективно, как и для контейнеров с элементами простых типов. Например: чаду!(1!з!<С!ив>ь !с) гурейе!' 1йг<С(иЬ>:: пега!ог !.С!! ЬС! р =Ялй Ц (1с.Ьеа!п (), 1с.епй(), С1иЬ ег!("Рглгпд РЬ11озорлегз" ) ); У... 18.4.3. «Арифметические» функциональные объекты. При работе с числовыми классами полезно представить стандартные арифметические функции в виде функциональных объектов (объектов-функций).
Для этого в файле <)(зпсг!опа!> стандартная библиотека определяет следующие операции: Мы могли бы при помощи ти!пр!!ез перемножить элементы двух векторов (получив в результате третий вектор): чо!й й!зсоипг(чес!ог<йоиЫе>з а, чесгог<йоиЫе>з Ь, чесгог<йоиЫе>ь гез) !галз!огт (а. Ьеа!и ( ), а .
елй ( ), Ь . Ьевйл ( ), Васа (пзеггег (гез), ти(ир1!ез<йоиЫе> ( ) ) Глава ) 8. Алгоритмы и классы функциональных объектов 620 Функциональный шаблон Ьасй 1изегтег() описывается в 819.2.4. Несколько численных алгоритмов можно найти в 822.6. ° 818.4.4.3 Здесь рассматриваются классы, которые адаптируют указатели на функции для их использования в качестве аргументов алгоритмов. ° 818.4.4.4 Здесь рассматриваются классы, позволяющие придать предикату противоположный смысл.
В совокупности эти классы часто именуются адаптерами (адар(ест)). Все они имеют общую структуру, опирающуюся на базовые классы влагу ~ииспои и ЬтпаГУ йтпе11оп (818.4.1). Для каждого из этих адаптеров предусмотрена вспомогательная функция, которая принимает исходный функциональный объект в качестве аргумента и возвращает требуемый функциональный объект. Последний с помощью метода орегагое() () выполняет необходимое действие. Можно сказать, что эти адаптеры реализуют функции высшего порядка, которые принимают на входе некоторые функции и порождают из них другие; .с ° Вызывает бинарную функцию с у в качестве второго аргумента Ь(пдгпа (у) Ыпдгпд Вызывает бинарную функцию с х в качестве первого аргумента Ыпдгзт (и) Ыпд1зт Вызывает 0-аргументную функцию-член через указатель тет ~пи() тет ~ип 1 ~ Вызывает унарную функцию-член через ~ тет ~ии1 1 ую константную казатель стантную казатель опзг 1 Более точно — прелнкатные адаптеры илн адаптеры преднкатов.
— Прим. ред. 18.4.4. Адаптеры («связывающие», для адаптирования функций-членов и указателей на функции, «отрицающие») Мы можем применять как собственные предикаты и арифметические функциональные объекты, так и те, что предоставляются стандартной библиотекой. Часто встречаются ситуации, когда необходимый предикат лишь незначительно отличается от уже существующих. Стандартная библиотека предоставляет для таких случаев ряд классов, позволяющих осуществлять композицию (адаптацию) объектов-функций: ° 818.4.4.1 Здесь рассматриваются классы, которые позволяют использовать двухаргументные (бинарные) функциональные объекты как одноаргументные (унарные) путем связывания (Ь1псйп8) одного из аргументов со значением. ° 818.4.4.2 Здесь рассматриваются классы, которые выступают как адаптеры функций-членов, позволяя использовать их в качестве аргументов алгоритмов.
) 8.4. Классы функциональных объектов 621 «Связывающие» и «отрицающие» адаптеры <!цпст(опа1> ю функцию-член " тот Гип ге~() Вызывает унарную функцию-член через ссылку. тот 7йл1 ге~' < Вызывает О-аргументную константную функцию-член через ссылку соля< тот 7ип ге7 < Вызывает унарную константную функцию-член через ссылку солта тат ~ил! геГ < Вызывает унарныи указатель на функцию.
Вызывает бинарный указатель на функцию рот<ег <о ипшу Типсйои ри <ип () ( ри сии() ротио <о Ьтагу у«испол Отрицает унарный предикат по<1 () ипагу лева<с Отрицает бинарный предикат. Ь<иа<у пваа<е <г () 18.4.4.1. «Связывающие» адаптеры Бинарные предикаты, такие как 1езз 518.4.2), полезны и достаточно гибки. Однако на практике чаще всего нужен предикат, который последовательно сравнивает заданное значение со значениями элементов контейнера. Функция 1езз <Ьап 7() (818.4) является типичным примером. Предикат же 1езз требует явного задания двух аргументов при каждом вызове, так что его нельзя применить в таких случаях непосредственно. Но мы можем определить шаблон 1«зз <Вал: <етр1«<с<с!азз Т> с!изз !езз <Ьап г риЫ!с ипагу7ипс<!оп <Т, Ьоо<> ( т Вг< риЫ1с г схрдсВ !езз <Ьап (солз< Та х): ага2 (х) ( ) Ьоо! орега<ог() (солт Та х) сопя< (ге<игл х<агд2< ) )' и использовать его: го!й1 (1!з«!и<>а с) ( Вз«!п<>г <с»из< пега<акр = <1п«(7(с.аед<л (),с.ел«(),!езз <Ьал«п<> (7) ) В ...