Функциональные объекты для операторов
Стандартная библиотека предоставляет набор готовых функциональных объектов, вызывающих имеющиеся в языке арифметические, логические и другие операторы. Предположим, требуется написать программу, обрабатывающую два вектора-операндов, формирующую третий вектор, элементы которого являются суммой элементов из векторов-операндов.
Для таких задач хорошо подходит алгоритм std::transform в бинарной форме, позволяющий применять некоторую бинарную операцию (не обязательно предикат!) к элементам двух последовательностей. В данном случае операцией является сложение. Вместо написания собственной функции или функтора для примитивного сложения, можно воспользоваться стандартным функтором std::plus.
Ниже приведен полный код реализации данной программы:
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>
// Вспомогательная функция для печати содержимого последовательности на консоль
template< typenameInputIt >
voidprintCollection ( InputIt _first, InputIt _last, const char* _prefix )
{
std::cout << _prefix;
while( _first != _last )
{
std::cout << * _first << ' ';
++ _first;
}
std::cout << std::endl;
}
int main()
{
// Формируем тестовые данные для двух векторов
std::vector< double> vX, vY;
for( inti = 0; i < 10; i++ )
{
vX.push_back( i );
vY.push_back( i * 2 );
}
// Результирующий вектор
std::vector< double> vR;
// Формируем результирующий вектор суммами элементов операндов
// vR[ i ] = vX[ i ] + vY[ i ]
std::transform(
vX.begin(), vX.end(), vY.begin(),
std::back_inserter( vR ),
std::plus< double>()
);
// Печатаем содержимое всех векторов
printCollection( vX.begin(), vX.end(), "X: " );
printCollection( vY.begin(), vY.end(), "Y: " );
printCollection( vR.begin(), vR.end(), "R: " );
}
В результате получим следующий вывод программы:
Использование функтора std::plus выглядит относительно простым. Стандартная библиотека С++ предоставляет аналогичные функторы для основных операторов:
Оператор | Стандартный функтор |
+ | std::plus |
- (бинарный) | std::minus |
- (унарный) | std::negates |
* | std::multiplies |
/ | std::divides |
% | std::modulus |
== | std::equal_to |
!= | std::not_equal_to |
< | std::less |
> | std::greater |
<= | std::less_equal |
>= | std::greater_equal |
&& | std::logical_and |
|| | std::logical_or |
! | std::logical_not |
& | std::bit_and |
| | std::bit_or |
^ | std::bit_xor |
~ | std::bit_not |
Связыватели std::bind
В некоторых случаях в распоряжении имеется готовая функция-предикат либо готовый функциональный объект, проверяющий условие, при этом возникает задача в противоположном условии (НЕ делится на 2, НЕ делится на 3). Безусловно, можно написать отдельные реализации для противоположных условий. Однако существует более рациональный подход к данной задаче.
Стандартная библиотека предоставляет ряд специальных функциональных объектов, называемых СВЯЗЫВАТЕЛЯМИ (binder), предназначенных для комбинирования вызываемых сущностей. До С++11 использование связывателей было достаточно сложным и имело массу ограничений, но в С++11 был добавлен универсальный связыватель std::bind (заголовочный файл <functional>). Такой связыватель позволяет вызывать любые методы и функции с произвольным количеством аргументов в едином стиле, и комбинировать их результаты, используя стандартные функторы.
Например, требуется подсчитать количество элементов вектора, которые не делятся на 2:
#include <functional>
#include <iostream>
#include <vector>
#include <algorithm>
// Функция определения делимости без остатка
booldividesOn ( intx, intN )
{
return( x % N ) == 0;
}
intmain ()
{
// Тест вектор
std::vector< int> v;
for( inti = 0; i < 10; i++ )
v.push_back( i );
// Подсчет количества чисел в векторе, которые НЕ делятся на 2
std::cout
<< std::count_if(
v.begin(), v.end(), // Поиск по всему вектору
// Связыватель верхнего уровня
std::bind(
// Логическое отрицание следующей вызываемой сущности
std::logical_not< bool>(),
// Связыватель нижнего уровня: делится ли число на 2
std::bind( & dividesOn, std::placeholders::_1, 2 )
)
)
<< std::endl;
}
Разберем данную конструкцию поэлементно. Связыватель создает специальный функциональный объект, применяющий некоторую операцию (любая вызываемая сущность), переданную первым аргументом, при этом с аргументами, передаваемыми остальными аргументами:
std::bind( & dividesOn, std::placeholders::_1, 2 )
Выражение std::placeholders::_1 (“местодержатель”) означает ссылку на первый аргумент, приходящий в функтор извне. Двойка - это константа, делимость на которую выясняется. Если функтор был бы бинарным, было бы возможно использовать как std::placeholders::_1, так и std::placeholders::_2 для второго аргумента, но данный случай унарный.
Чтобы лучше представить как такой связыватель работает, представим похожий по смыслу функтор:
structbinder_temp_functor
{