Bool operator() ( int_x ) const

ООП: Лекция 8. Стандартная библиотека шаблонов ч.2

Версия 3.01 22 августа 2016г.

(С) 2013-2016, Зайченко Сергей Александрович, к.т.н, ХНУРЭ, доцент кафедры АПВТ

Функциональные объекты

Использование функций для задания условий-предикатов либо для задания действий над элементами в алгоритме for_each далеко не всегда является удобным. В предыдущей лекции был рассмотрен пример подсчета количества чисел, делящихся без остатка на 2. Достаточно представить возрастание сложности пользовательского кода, если придется подсчитывать количество чисел, делящихся без остатка на 3, 4, 5, и т.д. Все перечисленные варианты потребуют уникальных функций для каждого конкретного случая, что неудобно, нерационально и неэстетично.

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

Стоит еще раз обратить внимание на способ, которым реализация алгоритма count_if использует свой предикат:

// Выясняем нужно ли засчитывать данный элемент у внешнего унарного предиката

if( up( * first ) )

++ result;

Внешне данный код выглядит как вызов функции. Обязан ли предикат быть функцией? Сущность идеи обобщенных концепций говорит о том, что если для конкретного фактического аргумента шаблона синтаксически допустим тот или иной способ обращения, значит такой аргумент может играть данную роль. Аналогичная схема уже приводилась при разъяснении понятия итераторов. Таким образом, можно сделать вывод, что если в качестве предиката внешним кодом будет предоставлено НЕЧТО, для которого допустим синтаксис функционального вызова, то это НЕЧТО может использоваться в качестве предиката. Такую обобщенную концепцию принято называть ВЫЗЫВАЕМОЙ СУЩНОСТЬЮ (callable entity).

Альтернативу функциям в роли вызываемой сущности составляют специальные виды объектов классов или структур, называемые ФУНКЦИОНАЛЬНЫМИ ОБЪЕКТАМИ (functional object), или ФУНКТОРАМИ (functor). Объект можно назвать функциональным, если он перегружает оператор функционального вызова (). Приведенная ниже структура перегружает такой оператор и возвращает значение true, если аргумент, передаваемый внешним алгоритмом делится без остатка на значение, переданное объекту в конструкторе:

structDividesOn

{

// Конструктор - принимает интересующий делитель

DividesOn ( int_divisor )

: m_divisor( _divisor ) {}

// Оператор функционального вызова

bool operator() ( int_x ) const

{

// Возвращает true, если переданное число делится без остатка на делитель

return_x % m_divisor == 0;

}

// Делитель

const intm_divisor;

};

Создать такие объекты можно с различными аргументами:

DividesOn dividesOn2( 2 );

DividesOn dividesOn3( 3 );

DividesOn dividesOn4( 4 );

Имея в распоряжении такие объекты, их можно вызывать как функции:

assert( dividesOn3( 6 ) );

assert( ! dividesOn4( 7 ) );

Ниже приведен модифицированный вариант программы, печатающий количества чисел в векторе, которые делятся без остатка на 2, на 3, на 4:

intmain ()

{

// Формируем вектор с тестовыми данными: 0 1 2 3 4 5 6 7 8 9

std::vector< int> v;

for ( inti = 0; i < 10; i++ )

v.push_back( i );

// Формируем функторы для делителей 2, 3, 4

DividesOn dividesOn2( 2 );

DividesOn dividesOn3( 3 );

DividesOn dividesOn4( 4 );

// Используем функторы в алгоритме count_if

std::cout

<< "Number of elements that divide on 2: "

<< std::count_if( v.begin(), v.end(), dividesOn2 )

<< ", on 3 : "

<< std::count_if( v.begin(), v.end(), dividesOn3 )

<< ", on 4 : "

<< std::count_if( v.begin(), v.end(), dividesOn4 )

<< std::endl;

}

Программа выдает следующие результаты:

Bool operator() ( int_x ) const - student2.ru

Наши рекомендации