count_if( vec.begin(), vec.end(), less_equal_value25());
(Другие примеры определения собственных объектов-функций можно найти в Приложении.)
Упражнение 12.4
Используя предопределенные объекты-функции и адаптеры, создайте объекты-функции для решения следующих задач:
(a)Найти все значения, большие или равные 1024.
(b)Найти все строки, не равные "pooh".
(c)Умножить все значения на 2.
Упражнение 12.5
Определите объект-функцию для возврата среднего из трех объектов. Определите функцию для выполнения той же операции. Приведите примеры использования каждого объекта непосредственно и путем передачи его функции. Покажите, в чем сходство и различие этих решений.
12.4. Еще раз об итераторах
Следующая реализация шаблона функции не компилируется. Можете ли вы сказать,
// в таком виде это не компилируется
template typename type
int
count( const vector &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 будет возвращен неконстантный, а для
vectorint::iterator iter0 = vec0.begin();
vec1 – константный итератор:
vector int ::const_iterator iter1 = vec1.begin();
Разумеется, присваивание константному итератору неконстантного разрешено всегда.
// правильно: инициализация константного итератора неконстантным
Например:
vector int ::const_iterator iter2 = vec0.begin();
12.4.1. Итераторы вставки
Вот еще один фрагмент программы, в котором есть тонкая, но серьезная ошибка.
int ia[] = { 0, 1, 1, 2, 3, 5, 5, 8 };
vector int ivec( ia, ia+8 ), vres;
// ...
// поведение программы во время выполнения не определено
Видите ли вы, в чем она заключается?
unique_copy( ivec.begin(), ivec.end(), vres.begin() );
Проблема вызвана тем, что алгоритм unique_copy() использует присваивание для копирования значения каждого элемента из вектора ivec, но эта операция завершится неудачно, поскольку в vres не выделено место для хранения девяти целых чисел. Можно было бы написать две версии алгоритма unique_copy(): одна присваивает элементы, а вторая вставляет их. Эта последняя версия должна, в таком случае, поддерживать вставку в начало, в конец или в произвольное место контейнера. Альтернативный подход, принятый в стандартной библиотеке, заключается в определении трех адаптеров, которые возвращают специальные итераторы вставки:
* back_inserter() вызывает определенную для контейнера операцию вставки push_back() вместо оператора присваивания. Аргументом back_inserter()
// правильно: теперь unique_copy() вставляет элементы с помощью
// vres.push_back()...
unique_copy( ivec.begin(), ivec.end(),
является сам контейнер. Например, вызов unique_copy() можно исправить, написав:
back_inserter( vres ) );
* front_inserter() вызывает определенную для контейнера операцию вставки push_front() вместо оператора присваивания. Аргументом front_inserter() тоже является сам контейнер. Заметьте, однако, что класс vector не поддерживает
// увы, ошибка: