Bool operator() ( int_argument1 ) const
{
returndividesOn( _argument1, 2 );
}
};
Безусловно, полноценная реализация намного сложнее, поскольку должна учитывать шаблонные типы аргументов, результатов, принимать операнды-константы через конструктор и сохранять их в полях на время существования функтора. Приведенный код лишь демонстрирует основную идею работы связывателя std::bind.
Связыватель верхнего уровня, в свою очередь, применяет операцию логического отрицания к результату выполнения связывателя нижнего уровня. Общий результат отправляется в алгоритм std::count_if и решает исходную задачу.
std::bind(
std::logical_not< bool>(),
std::bind( & dividesOn, std::placeholders::_1, 2 )
)
Средство std::bind очень гибкое и допускает массу вариантов. В качестве первого аргумента такого связывателя может использоваться любая вызываемая сущность. В качестве остальных аргументов могут быть представлены такие варианты:
● конечные вычисляемые выражения, например, константы;
● “местодержатели” std::placeholder;
● вложенные вызываемые сущности, в том числе вложенные связыватели std::bind.
Чтобы понять его истинную мощь, попробуем создать предикат, который выбирает числа делящиеся без остатка на 2, но не делящиеся на 3. Для этого понадобится такой вызов:
std::cout
<< std::count_if(
v.begin(), v.end(),
// Связыватель верхнего уровня
std::bind(
// Логическое И над двумя дочерними предикатами
std::logical_and< bool>(),
// Деление на 2
std::bind( & dividesOn, std::placeholders::_1, 2 ),
// Логическое отрицание деления на 3
std::bind(
std::logical_not< bool>(),
std::bind( ÷sOn, std::placeholders::_1, 3 )
)
)
)
<< std::endl;
Реализация универсального связывания std::bind и “местодержателей” основывается на формировании так называемых кортежей (std::tuple - N-мерной эволюции понятия std::pair), а также списков типов (typelists), и доступу к их элементам по индексам. Это достаточно сложные механизмы языка и стандартной библиотеки, далеко выходящие за рамки данного курса.
Возвращаясь к задаче о трансформировании векторов по формуле из предыдущего подраздела, попробуем реализовать преобразование по более сложной формуле:
Теоретически, такую формулу можно закодировать при помощи комбинации стандартных функторов и связывателей std::bind. Однако, переваривать такое “блюдо” рады немногие программисты:
// R = 3 * X / ( Y + 2 )
std::transform(
vX.begin(), vX.end(), vY.begin(),
std::back_inserter( vR ),
std::bind< double>(
std::divides< double>(), // деление
std::bind< double>( // числитель
std::multiplies< double>(),
3.0,
std::placeholders::_1
),
std::bind< double>( // знаменатель
std::plus< double>(),
std::placeholders::_2,
2.0
)
)
);
Хотя кодирование в таком стиле имеет своих фанатов, и цепочка связывателей всего лишь повторяет дерево вычисления выражения, а генерируемый машинный код весьма эффективен, большинство программистов сочтет такой код нечитабельным и неоднозначным. Сложность восприятия ограничивает применимость подобных приемов.