Ранее в этом совете уже упоминалось о том, что всюду, где STL ожидает получить предикатную функцию, может передаваться либо реальная функция, либо объект предикатного класса. Этот принцип действует в обоих направлениях. В любом месте, где STL рассчитывает получить объект предикатного класса, подойдет и предикатная функция (возможно, модифицированная при помощи ptr_fun
— см. совет 41). Теперь вы знаете, что функции operator
в предикатных классах должны быть «чистыми» функциями, поэтому ограничение распространяется и на предикатные функции. Следующая функция также плоха в качестве предиката, как и объекты, созданные на основе класса BadPredcate
:
bool anotherBadPredicate(const Widget&, const Widget&) {
static int timesCalled = 0; // Нет! Нет! Нет! Нет! Нет! Нет!
return ++timesCalled == 3; // Предикаты должны быть "чистыми"
} // функциями, а "чистые" функции
// не имеют состояния
Как бы вы ни программировали предикаты, они всегда должны быть «чистыми» функциями.
Предположим, у нас имеется список указателей Widget*
и функция, которая по указателю определяет, является ли объект Widget
«интересным»:
list
bool isInteresting(const Widget *pw);
Если потребуется найти в списке первый указатель на «интересный» объект Widget
, это делается легко:
list
isIntersting);
if (i != widgetPts.end) {
… // Обработка первого "интересного"
} // указателя на Widget
С другой стороны, если потребуется найти первый указатель на «неинтересный» объект Widget
, следующее очевидное решение не компилируется:
list
not1(isInteresting)); // Ошибка! He компилируется
Перед not1
к функции isInteresting
необходимо применить ptr_fun
:
list
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
.
Впрочем, not1
— не единственный компонент STL, предъявляющий подобные требования. Все четыре стандартных адаптера (not1
, not2
, bind1st
и bind2nd
), а также все нестандартные STL-совместимые адаптеры из внешних источников (например, входящие в SGI и Boost — см. совет 50), требуют существования некоторых определений типов. Объекты функций, предоставляющие необходимые определения типов, называются