Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

 else {

FwdIterator next=begin;

return remove_copy_if(++next, end, begin, p);

 }

}

Подробности нас сейчас не интересуют. Обратите внимание: предикат 

p
сначала передается
find_if
, а затем
remove_copy_if
. Конечно, в обоих случаях 
p
передается по значению — то есть копируется (теоретически возможны исключения, но на практике дело обстоит именно так; за подробностями обращайтесь к совету 38).

Первый вызов

remove_if
(расположенный
в клиентском коде, удаляющем третий элемент из
vw
) создает анонимный объект
BadPredcate
с внутренней переменной
timesCalled
, равной 0. Этот объект, известный в
remove_if
под именем
p
, затем копируется в
find_if
, поэтому
find_if
тоже получает объект
BadPredicate
с переменной
timesCalled
, равной 0. Алгоритм
find_if
«вызывает» этот объект, пока тот не вернет
true
; таким образом, объект вызывается три раза. Затем
find_if
возвращает управление
remove_if
.
Remove_if
продолжает выполняться и в итоге вызывает
remove_copy_if
, передавая в качестве предиката очередную копию
p
. Но переменная
timesCalled
объекта 
p
по-прежнему равна 0! Ведь алгоритм
find_if
вызывал не
p
, а лишь копию
p
. В результате при третьем вызове из
remove_copy_if
предикат тоже вернет
true
. Теперь понятно, почему
remove_if
удаляет два объекта
Widget
вместо одного.

Чтобы обойти эту лингвистическую ловушку, проще всего объявить функцию

operator
с ключевым словом
const
в предикатном классе. В этом случае компилятор не позволит изменить переменные класса:

class BadPredicate:

 public unary_function<Widget, bool> {

public:

 bool operator(const Widget&) const {

return ++timesCalled == 3; // Ошибка! Изменение локальных данных

 } // в константной функции невозможно

};

Из-за простоты этого решения я чуть было не озаглавил этот совет «Объявляйте

operator
константным в предикатных классах», но этой формулировки недостаточно. Даже константные функции могут обращаться к
mutablе
– переменным, неконстантным локальным статическим объектам, неконстантным статическим объектам класса, неконстантным объектам в области видимости пространства имен и неконстантным глобальным объектам. Хорошо спроектированный предикатный класс должен обеспечить независимость функций
operator
и от этих объектов. Объявление константных функций
operator
в предикатных классах необходимо для правильного поведения, но не достаточно. Правильно написанная функция
operator
является константной, но это еще не все. Она должна быть «чистой» функцией.

Ранее в этом совете уже упоминалось о том, что всюду, где STL ожидает получить предикатную функцию, может передаваться либо реальная функция, либо объект предикатного класса. Этот принцип действует в обоих

направлениях. В любом месте, где STL рассчитывает получить объект предикатного класса, подойдет и предикатная функция (возможно, модифицированная при помощи
ptr_fun
— см. совет 41). Теперь вы знаете, что функции
operator
в предикатных классах должны быть «чистыми» функциями, поэтому ограничение распространяется и на предикатные функции. Следующая функция также плоха в качестве предиката, как и объекты, созданные на основе класса
BadPredcate
:

bool anotherBadPredicate(const Widget&, const Widget&) {

 static int timesCalled = 0; // Нет! Нет! Нет! Нет! Нет! Нет!

 return ++timesCalled == 3; // Предикаты должны быть "чистыми"

} // функциями, а "чистые" функции

// не имеют состояния

Как бы вы ни программировали предикаты, они всегда должны быть «чистыми» функциями.

Совет 40. Классы функторов должны быть адаптируемыми

Предположим, у нас имеется список указателей

Widget*
и функция, которая по указателю определяет, является ли объект
Widget
«интересным»:

list<Widget*> WidgetPtrs;

bool isInteresting(const Widget *pw);

Если потребуется найти в списке первый указатель на «интересный» объект

Widget
, это делается легко:

list<Widget*>::iterator i = find_if(widgetPts.begin, widgetPts.end,

 isIntersting);

if (i != widgetPts.end) {

 … // Обработка первого "интересного"

} // указателя на Widget

С другой стороны, если потребуется найти первый указатель на «неинтересный» объект

Widget
, следующее очевидное решение не компилируется:

list<Widget*>::iterator i = find_if(widgetPtrs.begin, widgetPtrs.end,

 not1(isInteresting)); // Ошибка! He компилируется

Перед

not1
к функции
isInteresting
необходимо применить
ptr_fun
:

list<Widget*>::iterator i =

 find_if(widgetPtrs.begin, widgetPtrs.end,

 not1(ptr_fun(isInteresting))); // Нормально

if (i != widgetPtrs.end) { // Обработка первого

 … // "неинтересного" указателя

} //на Widget

При виде этого решения невольно возникают вопросы. Почему мы должны применять

ptr_fun
к isInteresting перед
not1
? Что
ptr_fun
для нас делает и почему начинает работать приведенная выше конструкция?

Ответ оказывается весьма неожиданным. Вся работа

ptr_fun
сводится к предоставлению нескольких определений типов. Эти определения типов необходимы для
not1
, поэтому применение
not1
к
ptr_fun
работает, а непосредственное применение
not1
к
isInteresting
не работает. Примитивный указатель на функцию
isInteresting
не поддерживает определения типов, необходимые для
not1
.

Поделиться:
Популярные книги

Как я строил магическую империю 4

Зубов Константин
4. Как я строил магическую империю
Фантастика:
боевая фантастика
постапокалипсис
аниме
фантастика: прочее
фэнтези
5.00
рейтинг книги
Как я строил магическую империю 4

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Попаданка 3

Ахминеева Нина
3. Двойная звезда
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Попаданка 3

Муж на сдачу

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Муж на сдачу

Призыватель нулевого ранга. Том 3

Дубов Дмитрий
3. Эпоха Гардара
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Призыватель нулевого ранга. Том 3

На границе империй. Том 10. Часть 5

INDIGO
23. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 5

Адвокат

Константинов Андрей Дмитриевич
1. Бандитский Петербург
Детективы:
боевики
8.00
рейтинг книги
Адвокат

На границе империй. Том 7

INDIGO
7. Фортуна дама переменчивая
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
6.75
рейтинг книги
На границе империй. Том 7

Здравствуй, 1985-й

Иванов Дмитрий
2. Девяностые
Фантастика:
альтернативная история
5.25
рейтинг книги
Здравствуй, 1985-й

О, Путник!

Арбеков Александр Анатольевич
1. Квинтет. Миры
Фантастика:
социально-философская фантастика
5.00
рейтинг книги
О, Путник!

Чужбина

Седой Василий
2. Дворянская кровь
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Чужбина

Бестужев. Служба Государевой Безопасности. Книга четвертая

Измайлов Сергей
4. Граф Бестужев
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга четвертая

Локки 5. Потомок бога

Решетов Евгений Валерьевич
5. Локки
Фантастика:
юмористическое фэнтези
аниме
фэнтези
5.00
рейтинг книги
Локки 5. Потомок бога

На границе империй. Том 10. Часть 4

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 4