private:
const T threshold;
public:
Meets Threshold(const T& threshold);
bool operator()(const WidgetS) const;
…
};
struct WidgetNameCompare:
std::binary_function{
bool operator()(const Widget& lhs, const Widget& rhs) const;
};
В обоих случаях типы, передаваемые unary_function
или binary_function
, совпадают с типами, получаемыми и возвращаемыми функцией operator()
класса функтора, хотя на первый взгляд несколько странно, что тип возвращаемого значения operator()
передается в последнем аргументе unary_function
или binary_function
.
Возможно, вы заметили, что MeetsTheshold
является классом, а WidgetNameCompare
является структурой. MeetsTheshold
обладает внутренним состоянием (переменная threshold
), и для инкапсуляции этих данных логично воспользоваться именно классом. WidgetNameCompare
состояния не имеет, поэтому и закрытые данные не нужны. Авторы классов функторов, в которых вся информация является открытой, часто объявляют структуры вместо классов — вероятно, только для того, чтобы им не приходилось вводить « public
» перед базовым классом и функцией operator()
. Выбор между классом и структурой при объявлении таких функторов определяется исключительно стилем программирования. Если вы еще не выработали собственного стиля и стараетесь имитировать профессионалов, учтите, что классы функторов без состояния в самой библиотеке STL (например, less
, plus
и т. д.) обычно записываются в виде структур.
Вернемся к определению WidgetNameCompare:
struct WidgetNameCompare:
std::binary_function< Widget, Widget, bool> {
bool operator()(const Widget& lhs, const Widget& rhs) const;
};
Хотя аргументы operator()
относятся к типу const Widget&
, шаблону binary_function
передается тип Widget
. Обычно при передаче unary_function
или binary_function
типов, не являющихся указателями, ключевые слова const
и знаки ссылки удаляются… только не спрашивайте, почему, — ответ на этот вопрос не интересен и не принципиален. Если вы сгораете от любопытства, напишите программу, в которой они не удаляются, и проанализируйте полученную диагностику компилятора. А если вы и после этого не утратите интерес к этой теме, посетите сайт boost.org (см. совет 50) и поищите на нем информацию об адаптерах объектов функций.
Если operator()
получает параметры-указатели, ситуация меняется. Ниже приведена структура, аналогичная WidgetNameCompare
, но работающая с указателями Widget*
:
struct PtrWidgetNameCompare:
std::binary_function , bool> {
bool operator()( const Widget*lhs, const Widget*rhs) const;
};
В этом случае типы, передаваемые binary_function
, совпадают с типами, передаваемыми operator()
. Общее правило для классов функторов, получающих или возвращающих указатели, заключается в том, что unary_function
или binary_function
передаются в точности те типы, которые получает или возвращает operator()
.
Помните, что базовые классы unary_function
и binary_function
выполняют только одну важную функцию — они предоставляют определения типов, необходимые для работы адаптеров, поэтому наследование от этих классов порождает адаптируемые объекты функций. Это позволяет использовать в программах следующие конструкции:
list widgets;
…
list::reverse_iterator i1 = // Найти последний объект
find_if(widgets.rbegin(), widgets.rend(), // Widget, не соответствующий
not1(MeetsThreshold(10))); // пороговому критерию 10
//(что бы это ни означало)
Widget w( аргументы конструктора ); // Найти первый объект Widget.
list::iterator i2 = // предшествующий w в порядке
find_if(widgets.begin(), widgets.end(), // сортировки, определенном
bind2nd(WidgetNameCompare(), w)); // WidgetNameCompare
Если бы классы функторов не определялись производными от unary_function
или binary_function
, ни один из этих примеров не компилировался бы, поскольку not1
и bind2nd
работают только с адаптируемыми объектами функций.
Объекты функций STL построены по образцу функций C++, а функции C++ характеризуются единственным набором типов параметров и одним типом возвращаемого значения. В результате STL неявно подразумевает, что каждый класс функтора содержит единственную функцию operator()
, типы параметров и возвращаемого значения которой должны передаваться unary_function
или binary_function
(с учетом правил передачи ссылок и указателей, о которых говорилось ранее). Из этого следует одно важное обстоятельство: не поддавайтесь соблазну и не пытайтесь объединять функциональность WidgetnNameCompare
и PtrWidgetCompare
в одной структуре с двумя функциями operator()
. В этом случае функтор будет адаптируемым по отношению лишь к одной из двух форм вызова (той, что использовалась при передаче параметров binary_function
), а пользы от такого решения будет немного — наполовину адаптируемый функтор ничуть не лучше неадаптируемого.
Читать дальше