Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 129
Текст из файла (страница 129)
Саязыватели Бинарные предикаты, такие как !екк 8 18.4.2), полезны и достаточно гибки. Однако мы скоро обнаружим, что самый полезный вид предиката — тот, который последовательно сравнивает фиксированный аргумент с элементами контейнера. Типичным примером является функция !екк 1йап 7 () (2 18А).
Операция !екк при каждом обрашенин требует двух явно указанных аргументов, поэтому ее нельзя применить непосредственно. Нам придется определить: 1етр!а1е<с!аяя Т> с!аяя !еяя !бал: риЬВс илагу Типсуоп<Т, Ьоо!> ( Т агу2; риЫ)с: ехрусгыеяя !бал (сопя! Тй х): агу2 (х) ( ) Ьоо! орегаяог () (соля1 Тй х) сопя1( ге!игл х<агу2; ) Теперь мы можем написать: оог<ТЯЫ<гп1 8 с) ( !1я1<!л1>, соля1 11егагог р= Для у (с.беугп "Е,сет! (), !еяя Гбао !пс>(7)); ll...
) 1етр1а 1е<с1 л яя Вт Ор> с!аяя Ылаег2пй :риЫгсипагу)илс1!ол<1урепатеВгпОр:агуител1 1уре, 1урелате ВтОр.теяи11 1уре> ( ргогес1е<1 В!лОр ор; 1урепате ВгпОр..яесопг! агуитеп1 1уре агу2; риЫ!с. Ыпйег2пи' (сопя1 В!пОрб х, сопя11урепате В!пОр: яесопй агуитеп1 1уреб о) ор (х), агу2 (о) ( ) Мы должны писать !екк 1йап<1п1> (7). а не!екк !пап (7), потому что аргуглент шаблона <(п1> не может быть вывелен нз типа аргумента конструктора (7) Я 13.3.1). Иредикат !екк !лап полезен в очень многих случаях. Важно то, сто мы определили его, зафиксировав илп связав второй аргумент предиката !еяа Такое построение со связыванием аргумента настолько употребительно, полезно и порой утомительно, что стандартная библиотека предоставляет для выполнения этой задачи стандартный класс: 583 18.4. Объекты-функции геяиИ !уре орега!ог() (соля!агуител! Гурейх) соля!(ге!игл ор (х, агу2);) !етр1аге<с1аяяВ(лОр, с1аяя Т Ь!лдег2лЫ<В!лОр> Ь!лИ2л<1 (соля!В!лОро ор, соля! Ть о) ге!игл Ь!л<1ег2лг!<В!лОр> (ор, о); Например, иы можем воспользоваться Ь!лс!2лг1 () для создания унарного предиката «меныпе чем 7» из бинарного предиката «меньше» н значения 7: оо!<! г" (Ия!<!л!>о с) ( Ия!<!л!>ссоля! дега!огр 1!л<! у (с.Ьеугл (), с.ел<1(), Ь!лг!2л<! (!езя< !от> (), 7)); //...
Читаема? Эффективно? Даже в среднего качества реализации С++ этот код будет эффективнее по быстродействию и памяти, чем первоначальная версия, использующая функцию 1еяя !Вал 7() из 2 18 4) Сравнение легко встраивается. Эта система обозначений логична, но к ней надо привыкнуть. Как правило, определение именованной операции со связанным аргументом в конце концов оправдывает все затратьц !етр1а!е<с1аяя Т> яггис! 1еяя !Иал: риЬИс Ь!лг!ег2л<1<1еяя<У» ( ехрйсп!езя гбао(соля! ТГ>х): Ь!лоег2лг!<!еяя<Т > (1еяя<Т (),х)(] оо!г! г" (Ия!<!лг>Ь с) ( Ия!<!л!> моля! !!его!огр =ваш«! у (сЬеу!л (), с.ел<1().
1еяя !Иал<шг> (?)); Важно определить 1еяя !бал в терминах 1еяя, а не прямым использованием оператора <. Таким образом, 1еяя !бал получит все преимушества любой специализации, которая может быть выполнена с 1еяя (8 13.5, ~ 19.2.2). По аналогии с Ь1лг!2лг1 () и Ь1лг(ег2лг1 () заголовочный файл </ллсИола1> предоставляет Ь(лг(1я! () н Ь!лг(ег! я! для связывания первого аргумента бинарной функции. Связывание аргумснта, Ь!лс!1я1 () и Ь(лИ2лс! () выполняет услугу, которую обычно называют Сиятушй (Карринг, по имени математика Н. В.
Сцггу), 18.4.4.2. Адаптеры функций-членов Большинство алгоритмов обращаются к стандартным или пользовательским операциям. Естественно, пользователи часто хотят вызвать функцию-член класса. Например: ооЫШат аИ (Ия!<БИаре*>й с) ( /ог еасИ (с.Ьеутл (), с.ел<! (), Г>эИаре ойгат), // ошобка! 584 Глава 18. Алгоритмы и объекты-функции Трудность в том, что функцию- глен тД() нужно вызывать для какого-нибудь объекта: р->т~(). Однако алгоритмы, такие как/ог еас!с (), вызывают свои функции-операнды простым применением скобокЯ]. Следовательно, нам нужен удобный и аффективный способ лля создания чего-то такого, что позволит алгоритму вызвать функцию-член.
Альтернативой было бы дублирование алгоритмов: одна версия для функций-членов и одна для обычных функций, Хуже того, нам могут понадобиться дополнительные версии алгоритмов для контейнеров с объектами (а не с указателями на объекты). Что касается связывателей Я 18А.4.1), эта проблема решается введением функции в класс. Сначала рассмотрим типичный случай, когда мы хотим вызвать функцию-член без аргументов для элементов контейнера с указателями: 1етр!а1е<с1азз К сlаяз Т» с1азз тет !ип 1: риЬИс ипагу йспс1!оп<Т', Р> ( Я(Т .*р>п)) (); риИс.
ехрйсптет лип 1(Р (Т р) ()):ртз'(р) () К орега1ог () (Т" р) сопя1 ( сети п (р — >'рт/) (), ) // вызов через указатель 1етр!а1е<с!аяя К с1аяя Т> тет!ап 1<К Т> тет /ип (Р (Т.*Я ()) ( геlигп тет !ип 1<К Т» (!); Этот помогает нам правильно записать пример с 3!!пресс!газе (): оо1«1с!гав аИ(Из1<йааре*>й1яр) //вызовбезаргументнойфункциа-члена // через у каза тель ( /ог еасЬ (!ярЬеу!и (), 1ярепд (), тет !ип (йВБаре: с!газе)) // рисуе и все фигуры Кроме того нам нужен некий класс и функция тет !ип () для обработки функции- члена с аргументом. Нам также нужны версии для прямого (а не через указатель) вызова объекта; они называются тет йгп ге/'().
Наконец, нам нужны версии для константных функций-членов: гетр!аге<с1аязК сlазз Т> тет /йп 1<К Т»тет /ип (Р (Т:*/) ()); Ои версии для упорных, // констатп них и константных упорных //функций-членов (слс таблицу в я !8 4.4) 1етр!аяе«с1аязКс(аязТ тет !ип ге/1<К Т тет !ип ге/(К (Тч*Я()); /1иверсиидля // унарных, константных и константных упорных //функций-членов(см, таблицу в я 18 4.4) Получив из <Типс1!опа!> эти адаптеры функций-членов, мы можем написать: ио1с!/(1сз1<я1гтд lя) // использование безаргумент ной функсйш-члено для объекта ( 1урейе/!ся1<я1ппу>л!ега1ог Е$1; // поиск "' ЕЯр=/(пй 1/(1я.беу1п 8,!кепд(),тет /ип геЯйя1ппу', етр1у)) оо1й го1а1е аИ (Изl«ЗЬаре' й Иь т1апу1е) 585 18.4.
Объекты-функции // использование функции -члена с одним аргул~внтол~ через указатель на ооъект Хог еасЬ (Ь Ьвут (), !я.впд (), Ыпс!2ис! (тат Хип (88Ьаре <го!а!е), апу(в)) ! Стандартной библиотеке не приходится иметь л ло с фупкциями-членами более чем одного аргумента, поскольку ни один из стандартных библиотечных алгоритмов не принимает в качестве операнда функцию более чем от двух аргументов. 18.4.4.3. Указатели на адаптеры функций Алгоритму нет дела, является ли «аргумент типа фуцкцииь функцией, указателем на функцию или объектом-функцией. Однако связывателю Я 18А.4.1) это не безразлично, поскольку ему нужно хранить копию аргумента для дальнейшего использования. Поэтому стандартная библиотека дает нам два адаптера, чтобы позволить использовать указатели на функции со стандартными алгоритмами из </ипс!!опа1>.
Определение и реализация очень напоминают определение и реализапито злаптеров функций-членов Я 18АА.2). Сновз используется пзра функций и пара классов: !етр1асе <с!аж А, с1аяя Р> рот!ег !в ипагу ?инс!!оп<А, Р>р!г !ип (Р (*з) (А)); !тпр!а!с<с!аяяА, с1аяяА2, с1аяя Р> ро(п!сг га Ь(пасу !ипсйап<А,А2, Р> ргггип (Р(*,/) (А,А2)); Получив эти адаптеры указателей на функцию, мы можем воспользоваться обычны- ми функциями вместе со связывателями: с!а як Рссо с<1 (,'* ... '/ ); Ьво1 лате Йву вд (сопя!РвсапР, сапягсЬаг*); //сравнение, основанное на ил~снах Ьаа1 вял яву еу(саля!Ревах<И, соля!1апу); //сравнение, основанное на числах иаиЬЯМ<Ресогд> 8!с) О использование указателя на функцию ( !уреде/1гя!<Ревах<1>: Нега!аг?Л; 1Хр =/!лд (/(!г Ьсу1п (), 1г слд (), Ыл<12л<1 (р!г,!йл (патв Ьву ву), "Джон Браун" )); Е?д=,!Ьи! К(!гусу!л (), !кепс!(), Ылд2л<1(1згг !йп (яяп Ьву ед), 12учуб?890)); Так в списке 1г ищутся элементы, соответствую~дне ключам "Дзгсоп Браун" и 123455 7890.
18.4.4.4. Отрицатели Предикатные отрицатели сродни связывателям в том, что они принимают операцию и производят из нее другую операцию. Определение н реализация отрицателей следуют модели адаптеров функций-членов (ф 18АА.2). Их определения тривиакьны, но окончательной простоте мешают длинные стандартные имена: гетр!а! е<с1 а як Рге<1> с1аяя ипагу пеуаге.риЫ!сипагу /илснап<гуряпате Ргедзагуитеп! гуре, Ьаа1> ( Рге<1 ар; 588 Глава 18.
Алгоритмы и объекты-функции риЫ!а ехр!!с!1 ипогу пеуа1е )сопи! ргейй р) ор )р) () Ьоо! орега1ог )) )сопи! агуитеп1 !урей х) сопЯ ге1игп ~ор )к),' ) ), 1етр1а1е<с!аяя Ргей> и!оии 6тагу пеуа1е: риЫ!с Ь!пагу /ипсг!оп<1урепатергей:,$мй агуитеп1 1уре, !урепатеРгейззесопй агуитеп1 1уре, Ьоо!>( гурейеЦ!гз1 агуитеп! 1уре Агу; 1урейе/иесопй агуитеп1 1уреАгу2; Ргей ор, риЫ!с ехр!!с!! 6!вагу пеуа1е )сопя! Ргеййр): ор )р) 1) Ьоо! орега1ог)) )сопя1Агуйх, сопя! Агу2й у) сопя1 ( ге!ига!ор )х, у); ) 1етр!аге<е1аии Реей> ипагу пеуаге<Ргей> по1! )сопи1Ргейй р); //заиеняет унарныд предикат на противоположнын! 1етр!а1е<с!аизргей> Ыпагу пеуа1е<Ргей> по!2)сопз1ргейй р); // заяеняет бинарный вреди кот на противоположный Зтн классы и функции объявлены в </ипс1!опа!>.
Имена /!гз! агуитеп! 1уре, яесопй агдитеп! 1уре и т. п. взяты из стандартных базовых классов ипагу ~ипс!!оп и Ь!пагу /ипс1!оп. Как и связыватели, отрицатели очень удобно использовать косвенно через их вспомогательные функции. Например, мы можем описать бинарный предикат «не меньше чем» и использовать его для поиска первой пары соответствующих элементов, чей первый элемент больше или равен второму: оо!й/) оесгог<и!1> й Ы, !!я1<!п1> й !!) // перегзяотренный прнляер из в ! 84.2 //... р1 = т!ята1сл )о! Ьеут )), обепйО, у Ьеу!и )), по!2 )!еиз<иМ> )))); //... ) То есть р1 обозначает первую пару элементов, для которых не выполнился предикат «не меньше». 1)рсдыкаты имеют дело с логическими условиями, поэтому здесь нет эквпвю!ентов для битовых операций), й, " и —.