Could you explain how does this code work. It successfully counts vector elements with even values, but it's not clear for me how does binding in this particular case work.
count_if(vec.begin(), vec.end(),
std::bind(logical_not<bool>(),
std::bind(modulus<int>(), placeholders::_1, 2)));
Note that the code you posted counts the even numbers in the vector, not the odd ones:
count_if(vec.begin(), vec.end(), bind(logical_not<bool>(), bind(modulus<int>(), placeholders::_1, 2)));
The count_if
is an algorithm that returns the number of elements in the specified range satisfying specific criteria:
count_if(first, last, criteria)
In your case, first
is vec.begin()
and last
is vec.end()
: so the whole vector is considered for the count.
Now let's focus the attention on the criteria part.
Starting from inner and going outer:
modulus<int>
is a function object that returns the remainder of an integer division (just like %
operator). It takes two arguments: the first is expressed as placeholders::_1
, which is the generic element in the source vector. Think of it as a variable x
that scans the entire vector content.
The second argument is the number 2
, since to check if an integer is even or odd, you can calculate x % 2
and compare the result with 0:
x % 2 == 0 --> even number
x % 2 == 1 --> odd number
bind
is used to specify the arguments of the modulus
function object.
The result of this modulus operation is given as input to another function object: logical_not<bool>
. This just negates the input, e.g. if the input was false
(0), logical_not<bool>
returns true
, and viceversa.
So, the "counting criteria" is expressed by this flow of operations:
placeholders::_1 % 2
, i.e. <<generic vector element>> % 2
, using modulus
.0
(false), return true
(and viceversa), using logical_not
.So, if a number is even:
even number % 2 == 0
true
.Instead, if a number is odd:
odd number % 2 == 1
false
.Since count_if
counts the number of elements for which the criteria is true
, you are counting the even numbers in the vector.
If you really want to count the odd numbers in the vector, you can just get rid of the logical inversion (i.e. logical_not
):
auto odds = count_if(vec.begin(), vec.end(),
bind(modulus<int>(), placeholders::_1, 2));
Note that in this case the functional approach using modulus
and logical_not
seems too complicated: using a lambda (or even an ad hoc IsEven()
simple function) would be clearer.
Consider the following code (live here on Ideone) for a comparison between the three approaches:
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
using namespace std;
bool IsEven(int n) {
return (n % 2) == 0;
}
int main() {
// Test vector
vector<int> vec{ 11, 22, 33, 44, 55 };
// Using functional approach
auto n = count_if(vec.begin(), vec.end(),
bind(logical_not<bool>(),
bind(modulus<int>(), placeholders::_1, 2)));
cout << n << endl;
// Using lambdas
n = count_if(vec.begin(), vec.end(),
[](int n) { return (n % 2) == 0; });
cout << n << endl;
// Using boolean returning ad hoc function
n = count_if(vec.begin(), vec.end(), IsEven);
cout << n << endl;
}
Unravel from inner call to outer call:
modulus<int>( a, 2 )
returns the remainder of something after a division by 2: ==0 or !=0.
logical_not<bool>( x )
does a logical inversion of x (so 0/false becomes 1/true and 1/true becomes 0/false)
count_if(from, to, cond )
evaluates cond (the inverted modulus) for all elements defined by the iterator from/to.
And the placeholders::_1
is the hardwired way for inserting something determined in the iterator-driven loop run by count_if
(i.e., the current element) into the function nested down below.
This doesn't count odd elements but even elements.
To count vector elements with odd values we have to check each element for division by 2 and return true if the result is 1.
So we will use the modulus(), which is a function object that implements operator()
constexpr T operator()( const T& lhs, const T& rhs ) const;
and returns the remainder of the division of lhs by rhs.
We have to use std::bind to glue one and only one argument passed to
count_if( InputIt first, InputIt last, UnaryPredicate p )
unary predicate p
(which is our modulus here) to first argument of modulus and constant 2
to second
std::bind(modulus<int>(), placeholders::_1, 2))
Now our function std::bind(modulus<int>(), placeholders::_1, 2))
returns true (1) if argument is odd and false (0) if argument is even. If we want to count even arguments we must neglect this, so our predicate must return the opposite:
std::bind(logical_not<bool>(),
std::bind(modulus<int>(), placeholders::_1, 2))
http://ideone.com/80VmsZ
As Mike Seymour suggested, simpler and cleaner design would be to replace this binds with short lambda function:
[](int x){return x % 2 == 0;} // to count even elements
[](int x){return x % 2 != 0;} // to count odds
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With