Функциональные объекты
Рассмотрим четвертый тип компонентов библиотеки STL, а именно, функциональные объекты. Можно сказать, что функциональный объект – это объект класса, в котором определен метод operator(). Данные объекты являются обобщением указателей на функцию. Напомним, что сама по себе функция не является переменной. Однако можно определить указатели на функции, которые разрешено присваивать, хранить в массивах, назначать полями структур, передавать в функции и возвращать из функций. Функциональные объекты, в некотором смысле, являются обобщением указателей на функцию и предоставляют больше гибкости. Функциональный объект сам по себе является некоторым типом данных и поэтому может передаваться в функцию и возвращаться из функции. Более того, обобщенные алгоритмы STL содержат шаблонные параметры, которые инстанцируются функциональными объектами. Рассмотрим, например, алгоритм, вычисляющий сумму элементов последовательности:
template <typename Inputlterator, typename T> T accumulate(Inputlterator first, Inputlterator last, T init)
{
while (first != last)
init = init + *first; ++first;
return init;
}
Данная функция, будучи вызвана с итераторамиfirst иlast и значениемinit, прибавляет кinit значения в позициях, начиная с first и заканчиваяlast (не включая последнюю), и возвращает получившуюся сумму.
Теперь предположим, что вместо сложения мы захотим использовать умножение, или деление, или какую-либо другую операцию, определенную для того или иного класса и использующую два аргумента. Для этого реализована вторая версия алгоритма:
template <typename Inputlterator, typename T,
typename BinaryOperation>
T accumulate(Inputlterator first, Inputlterator last, T init, BinaryOperation binary_op)
{
while (first != last)
init = binary_op(init,*first); ++first;
return init;
}
Здесь в качестве операции может использоваться любой объект, в котором перегруженный метод operator() принимает два аргумента типа T и возвращает значение, типа T.
Для того, чтобы определять свои функциональные объекты нужно использовать классы, объявленные в стандартной библиотеке и находящиеся в заголовочном файле <functional>. Таких классов два. Первый класс является основой для создания унарных функциональных объектов (унарных функций):
template <typename Arg, typename Result> struct unary_function
{
typedef Arg argument_type; typedef Result result type;
};
И второй класс служит основой для реализации бинарного функционального объекта (бинарной функции):
template <typename Argl, typename Arg2, typename Result> struct binary_function
{
typedef Argl first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type;
};
Класс, создающий унарный функциональный объект должен наследоваться от unary_function, а класс, реализующий бинарный функциональный объект, наследуется от binary_function. В общем случае они должны наследоваться как public, что для структур принято по умолчанию. Это позволяет определять стандартные имена типов для аргументов функции и типа возврата.
Структуры unary_function и binary_function предоставляют typedef для типов аргументов и типа возврата функционального объекта. Эти имена используются некоторыми адаптерами и могут пригодиться в других случаях. Таким образом, следует использовать эти имена и в создаваемом функциональном объекте. Другими словами, вы должны использовать result_type в качестве типа возврата для operator(). Кроме того необходимо использовать argument_type как тип аргумента дляoperator()для унарного функционального объекта и first_argument_type, second argument_type в качестве типов аргументов для бинарного функционального объекта. Таким образом, общая форма operator() выглядит так:
result_type operator (argument_type arg);
result_type operator(first_argument_type argl, second_argument_type arg2);
Функциональный объект не должен создавать побочных эффектов. Другими словами, он не должен предпринимать действий, не связанных с его основным предназначением. Например, функциональный объект, чье назначение - сравнивать два элемента на эквивалентность, не должен модифицировать ни одного из элементов в процессе своей работы.
Приведем пример унарного функционального объекта (унарной функции), который принимает в качестве аргумента переменную x типа double и возвращает значение 1/x также типа double.
class reciprocal:public std::unary_function<double,double>
{
public:
result_type operator() (argument_type val);
};
Ее реализация выглядит так:
std::unary_function<double,double>::result_type reciprocal::operator() (std::unary_function<double,double>::argument_type val)
{
if(val==0.00)
{
return 0.00;
}
else
{
return 1.00/val;
}
}
Следует обратить внимание на синтаксис: тип возвращаемого значения – std::unary_function<double,double>::result_type и тип аргумента – std::unary_function<double,double>::argument_type. И то и другое в данном случае является синонимом типа double.
Экземпляр данного объекта используется как функция:
double x=5.00;
reciprocal fun;
double y=fun(x);
Важно!!! В отличие от функций, использовать функциональный объект можно только после его создания!
Приведем пример бинарного функционального объекта по имени midpoint, который вычисляет среднее между двумя значениями. Его объявление выглядит так:
class midpoint:public std::binary_function<int,int,double>
{
public:
result_type operator()(first_argument_type a1, second_argument_type a2);
};
Соответственно реализация
std::binary_function<int,int,double>::result_type midpoint::operator()(std::binary_function<int,int,double>:: first_аrgument_type a1, std::binary_function<int,int,double>::second_argument_type a2)
{
return static_cast<double>(a1+a2)/2;
}
Тип возвращаемого значения double в данном случае имеет синоним std::binary_function<int,int,double>::result_type, а тип аргументов int – синонимы std::binary_function<int,int,double>::first_argument_type и std::binary_function<int,int,double>::second_argument_type.
Библиотека STL предоставляет некоторые встроенные функциональные объекты. Часть из них реализуют базовые арифметические операции:
STL предоставляет базовые классы функциональных объектов для всех арифметических операторов языка. Функциональность операторов описывается далее: template <typename Т> struct plus;
принимает два операнда типаТ и возвращает их сумму;template <typename Т> struct minus;
принимает два операнда типаТи возвращает результат вычитания второго операнда из первого;template <typename Т> struct multiplies;
принимает два операнда типаТи возвращает их произведение;template <typename Т> struct divides;
принимает два операнда типаТи возвращает результат деления первого операнда на второй; template <typename Т> struct modulus;
принимает два операндахиу, типаТи возвращает результат вычисления х%у.template <typename Т> struct negate;
унарный функциональный объект, который принимает единственный операнд типаТи возвращают значение с обратным знаком.
Эти классы также являются наследниками от класса binary_function, а класс negate – наследник класса unary_function. Например:
template <typename Т> struct multiplies:binary_function<T,T,T>
{ T operator()(const T &a, const T &b)const;};
и
template <typename Т> struct multiplies:unary_function<T,T>
{ T operator()(const T &a)const;};
Напомним, что использовать функциональные объекты можно только после их создания. Например,
multiplies<int> M;
В библиотеке STL также содержатся функциональные объекты, называемые предикатами. Предикат – функциональный объект, возвращающий значение типа bool. Такими объектами являются операции сравнения и логические операции.
template <typename Т> struct equal_to;
Принимает два параметра,x и у, типаТи возвращаетtrue, еслих==у; в противном случае возвращаетfalse.
template <typename Т> struct not_equal_to;
Принимает два параметра,х и у, типаТи возвращает true, еслих!=у;в противном случае возвращаетfalse.
template <typename Т> struct greater;
Принимает два параметра,х иу, типаТи возвращаетtrue, еслих>у; впротивном случае возвращаетfalse.
template <typename Т> struct less;
Принимает два параметра,х и у, типаТи возвращаетtrue, еслих<у; в противном случае возвращает false.
template <typename Т> struct greater_equal;
Принимает два параметра,х и у, типаТ и возвращаетtrue, если х>=у; в противном случае возвращаетfalse.
Принимает два параметра,х иу, типаТ и возвращает true, еслих>= у, в противном случае возвращает false.
template <typename Т> struct less_equal;
Принимает два параметра,х иу, типаТи возвращает true, еслих<=у; впротивном случае возвращаетfalse.
template <typename T> struct logical_and;
Принимает два параметра,x иу, типа Т и возвращает результат типа bool логической операциии:х&&у.
template <typename Т> struct logical_or;
Принимает два параметра,х иу, типаТи возвращает результат типа bool логической операцииили:х||у.
template <typename Т> struct logical_not;
Принимает один параметрх типаТ и возвращает результат типаbool логической операциине:!х.