С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 97
Текст из файла (страница 97)
Сравнительные объекты-функцииСравнительные объекты-функции поддерживают операции равенства, неравенства,больше, больше или равно, меньше, меньше или равно.equal_to<string> stringEqual;sres = stringEqual( sval1, sval2 );ires = count_if( svec.begin(), svec.end(),•Равенство: equal_to<Type>equal_to<string>(), sval1 );not_equal_to<complex> complexNotEqual;cres = complexNotEqual( cval1, cval2 );ires = count_if( svec.begin(), svec.end(),•Неравенство: not_equal_to<Type>not_equal_to<string>(), sval1 );greater<int> intGreater;ires = intGreater( ival1, ival2 );ires = count_if( svec.begin(), svec.end(),•Больше: greater<Type>greater<string>(), sval1 );greater_equal<double> doubleGreaterEqual;dres = doubleGreaterEqual( dval1, dval2 );ires = count_if( svec.begin(), svec.end(),•Больше или равно: greater_equal<Type>С++ для начинающих562greater_equal <string>(), sval1 );less<Int> IntLess;Ires = IntLess( Ival1, Ival2 );ires = count_if( svec.begin(), svec.end(),•Меньше: less<Type>less<string>(), sval1 );less_equal<int> intLessEqual;ires = intLessEqual( ival1, ival2 );ires = count_if( svec.begin(), svec.end(),•Меньше или равно: less_equal<Type>less_equal<string>(), sval1 );12.3.4.
Логические объекты-функцииЛогические объекты-функции поддерживают операции “логическое И” (возвращает true,если оба операнда равны true, – применяет оператор &&, аcсоциированный с типомType), “логическое ИЛИ” (возвращает true, если хотя бы один из операндов равенtrue, – применяет оператор ||, аcсоциированный с типом Type) и “логическое НЕ”(возвращает true, если операнд равен false, – применяет оператор !, аcсоциированныйс типом Type)logical_and<int> intAnd;ires = intLess( ival1, ival2 );•Логическое И: logical_and<Type>dres = BinaryFunc( logical_and<double>(), dval1, dval2 );logical_or<int> intSub;ires = intSub( ival1, ival2 );•Логическое ИЛИ: logical_or<Type>dres = BinaryFunc( logical_or<double>(), dval1, dval2 );logical_not<Int> IntNot;ires = IntNot( Ival1, Ival2 );•Логическое НЕ: logical_not<Type>dres = UnaryFunc( logical_or<double>(), dval1 );С++ для начинающих12.3.5.
Адаптеры функций для объектов-функцийВ стандартной библиотеке имеется также ряд адаптеров функций, предназначенных дляспециализации и расширения как унарных, так и бинарных объектов-функций.Адаптеры – это специальные классы, разбитые на следующие две категории:•связыватели (binders). Это адаптеры, преобразующие бинарный объект-функцию вунарный объект, связывая один из аргументов с конкретным значением. Например,для подсчета в контейнере всех элементов, которые меньше или равны 10, следуетпередать алгоритму count_if() объект-функцию less_equal, один из аргументовкоторого равен 10.
В следующем разделе мы покажем, как это сделать;•отрицатели (negators). Это адаптеры, изменяющие значение истинности объектафункции на противоположное. Например, для подсчета всех элементов внутриконтейнера, которые больше 10, мы могли бы передать алгоритму count_if()отрицатель объекта-функции less_equal, один из аргументов которого равен 10.Конечно, в данном случае проще передать связыватель объекта-функции greater,ограничив один из аргументов со значением 10.В стандартную библиотеку входит два предопределенных адаптера-связывателя: bind1stи bind2nd, причем bind1st связывает некоторое значение с первым аргументомбинарного объекта-функции, а bind2nd – со вторым. Например, для подсчета внутриконтейнера всех элементов, которые меньше или равны 10, мы могли бы передатьcount_if( vec.begin(), vec.end(),алгоритму count_if() следующее:bind2nd( less_equal<int>(), 10 ));В стандартной библиотеке также есть два предопределенных адаптера-отрицателя: not1 иnot2.
not1 инвертирует значение истинности унарного предиката, являющегосяобъектом-функцией, а not2 – значение бинарного предиката. Для отрицаниярассмотренного выше связывателя объекта-функции less_equal можно написатьcount_if( vec.begin(), vec.end(),следующее:not1( bind2nd( less_equal<int>(), 10 )));Другие примеры использования связывателей и отрицателей приведены в Приложении,вместе с примерами использования каждого алгоритма.12.3.6. Реализация объекта-функцииПри реализации программы в разделе 12.2 нам уже приходилось определять рядобъектов-функций. В этом разделе мы изучим необходимые шаги и возможные вариациипри определении класса объекта-функции.
(В главе 13 определение классарассматривается детально; в главе 15 обсуждается перегрузка операторов.)563С++ для начинающихВ самой простой форме определение класса объекта-функции сводится к перегрузкеоператора вызова. Вот, например, унарный объект-функция, определяющий, что// простейшая форма класса объекта-функцииclass less_equal_ten {public:bool operator() ( int val ){ return val <= 10; }некоторое значение меньше или равно 10:};Теперь такой объект-функцию можно использовать точно так же, как предопределенный.Вызов алгоритма count_if() с помощью нашего объекта-функции выглядит следующимобразом:count_if( vec.begin(), vec.end(), less_equal_ten() );Разумеется, возможности этого класса весьма ограничены. Попробуем применитьcount_if( vec.begin(), vec.end(),отрицатель, чтобы подсчитать, сколько в контейнере элементов, больших 10:not1(less_equal_then ()));или обобщить реализацию, разрешив пользователю задавать значение, с которым надосравнивать каждый элемент контейнера.
Для этого достаточно ввести в класс член дляхранения такого значения и реализовать конструктор, инициализирующий данный членclass less_equal_value {public:less_equal_value( int val ) : _val( val ) {}bool operator() ( int val ) { return val <= _val; }private:int _val;указанной пользователем величиной:};Новый объект-функция применяется для задания произвольного целого значения.Например, при следующем вызове подсчитывается число элементов, меньших илиравных 25:count_if( vec.begin(), vec.end(), less_equal_value( 25 ));Разрешается реализовать класс и без конструктора, если параметризовать его значением,с которым производится сравнение:564С++ для начинающих565template < int _val >class less_equal_value {public:bool operator() ( int val ) { return val <= _val; }};Вот как надо было бы вызвать такой класс для подсчета числа элементов, меньших илиравных 25:count_if( vec.begin(), vec.end(), less_equal_value<25>());(Другие примерыПриложении.)определениясобственных объектов-функцийможно найтивУпражнение 12.4Используя предопределенные объекты-функции и адаптеры, создайте объекты-функциидля решения следующих задач:(a) Найти все значения, большие или равные 1024.(b) Найти все строки, не равные "pooh".(c) Умножить все значения на 2.Упражнение 12.5Определите объект-функцию для возврата среднего из трех объектов.
Определитефункцию для выполнения той же операции. Приведите примеры использования каждогообъекта непосредственно и путем передачи его функции. Покажите, в чем сходство иразличие этих решений.12.4. Еще раз об итераторахСледующая реализация шаблона функции не компилируется. Можете ли вы сказать,// в таком виде это не компилируетсяtemplate < typename type >intcount( const vector< type > &vec, type value ){int count = 0;vector< type >::iterator iter = vec.begin();while ( iter != vec.end() )if ( *iter == value )++count;return count;почему?}Проблема в том, что у ссылки vec есть спецификатор const, а мы пытаемся связать с нейитератор без такого спецификатора.
Если бы это было разрешено, то ничто не помешалобы нам модифицировать с помощью этого итератора элементы вектора. ДляС++ для начинающихпредотвращения подобной ситуации язык требует, чтобы итератор, связанный с const-// правильно: это компилируется без ошибоквектором, был константным. Мы можем сделать это следующим образом:vector< type>::const_iterator iter = vec.begin();Требование, чтобы с const-контейнером был связан только константный итератор,аналогично требованию о том, чтобы const-массив адресовался только константнымуказателем.
В обоих случаях это вызвано необходимостью гарантировать, чтосодержимое const-контейнера не будет изменено.Операции begin() и end() перегружены и возвращают константный или неконстантныйитератор в зависимости от наличия спецификатора const в объявлении контейнера. Еслиvector< int > vec0;дана такая пара объявлений:const vector< int > vec1;то при обращениях к begin() и end() для vec0 будет возвращен неконстантный, а дляvector< int >::iterator iter0 = vec0.begin();vec1 – константный итератор:vector< int >::const_iterator iter1 = vec1.begin();Разумеется, присваивание константному итератору неконстантного разрешено всегда.// правильно: инициализация константного итератора неконстантнымНапример:vector< int >::const_iterator iter2 = vec0.begin();12.4.1.