Функциональные объекты для операторов

 

Стандартная библиотека предоставляет набор готовых функциональных объектов, вызывающих имеющиеся в языке арифметические, логические и другие операторы. Предположим, требуется написать программу, обрабатывающую два вектора-операндов, формирующую третий вектор, элементы которого являются суммой элементов из векторов-операндов.

 

Для таких задач хорошо подходит алгоритм 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

{