Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
The <functional> header defines a number of useful generic function objects. They are admittedly simple, but you can use them to compose
more complicated function objects. Consequently, in many instances, you can
construct complicated predicates without writing a single function. You do so
by using function object adaptors to take the simple function objects and adapt them for use with other function
objects in a chain of operations.
To illustrate, let s use only standard function objects to
accomplish what gt15( ) did earlier. The standard function object, greater, is a binary function object that returns true if its first
argument is greater than its second argument. We cannot apply this directly to
a sequence of integers through an algorithm such as remove_copy_if( )
because remove_copy_if( ) expects a unary predicate. We can
construct a unary predicate on the fly that uses greater to compare its
first argument to a fixed value. We fix the value of the second
parameter at 15 using the function object adaptor bind2nd, like this:
//: C06:CopyInts4.cpp
// Uses a standard function object and adaptor.
#include <algorithm>
#include <cstddef>
#include <functional>
#include <iostream>
#include <iterator>
using namespace std;
int main() {
int a[] = { 10, 20, 30 };
const size_t SIZE = sizeof a / sizeof a[0];
remove_copy_if(a, a + SIZE,
ostream_iterator<int>(cout,
"\n"),
bind2nd(greater<int>(), 15));
} ///:~
This program produces the same result as CopyInts3.cpp,
but without writing our own predicate function gt15( ). The
function object adaptor bind2nd( ) is a template function that
creates a function object of type binder2nd, which simply stores the two arguments passed to bind2nd( ), the first of which must be a
binary function or function object (that is, anything that can be called with
two arguments). The operator( ) function in binder2nd, which is itself a unary function, calls the binary function it stored, passing it its
incoming parameter and the fixed value it stored.
To make the explanation concrete for this example, let s
call the instance of binder2nd created by bind2nd( ) by the
name b. When b is created, it receives two parameters (greater<int>( )
and 15) and stores them. Let s call the instance of greater<int>
by the name g, and call the instance of the output stream iterator by
the name o. Then the call to remove_copy_if( ) earlier conceptually
becomes the following:
remove_copy_if(a, a + SIZE, o, b(g, 15).operator());
As remove_copy_if( ) iterates through the sequence, it calls b on each element, to determine whether to ignore
the element when copying to the destination. If we denote the current element
by the name e, that call inside remove_copy_if( ) is
equivalent to
but binder2nd s function call operator just turns
around and calls g(e,15), so the earlier call is the same as
which is the comparison we were seeking. There is also a bind1st( ) adaptor that creates a binder1st object, which fixes the first argument of the associated input binary function.
As another example, let s count the number of elements in
the sequence not equal to 20. This time we ll use the algorithm count_if( ),
introduced earlier. There is a standard binary function object, equal_to, and also a function object adaptor, not1( ), that takes a unary function object as a parameter and invert its truth value. The following
program will do the job:
//: C06:CountNotEqual.cpp
// Count elements not equal to 20.
#include <algorithm>
#include <cstddef>
#include <functional>
#include <iostream>
using namespace std;
int main() {
int a[] = { 10, 20, 30 };
const size_t SIZE = sizeof a / sizeof a[0];
cout << count_if(a, a + SIZE,
not1(bind1st(equal_to<int>(),
20)));// 2
} ///:~
As remove_copy_if( ) did in the previous
example, count_if( ) calls the predicate in its third argument (let s
call it n) for each element of its sequence and increments its internal
counter each time true is returned. If, as before, we call the current
element of the sequence by the name e, the statement
in the implementation of count_if is interpreted as
if(!bind1st(equal_to<int>, 20)(e))
which ends up as
if(!equal_to<int>(20, e))
because not1( ) returns the logical negation of
the result of calling its unary function argument. The first argument to equal_to
is 20 because we used bind1st( ) instead of bind2nd( ).
Since testing for equality is symmetric in its arguments, we could have used
either bind1st( ) or bind2nd( ) in this example.
The following table shows the templates that generate the
standard function objects, along with the kinds of expressions to which they
apply:
Name
|
Type
|
Result produced
|
plus
|
BinaryFunction
|
arg1 + arg2
|
minus
|
BinaryFunction
|
arg1 - arg2
|
multiplies
|
BinaryFunction
|
arg1 * arg2
|
divides
|
BinaryFunction
|
arg1 / arg2
|
modulus
|
BinaryFunction
|
arg1 % arg2
|
negate
|
UnaryFunction
|
- arg1
|
equal_to
|
BinaryPredicate
|
arg1 == arg2
|
not_equal_to
|
BinaryPredicate
|
arg1 != arg2
|
greater
|
BinaryPredicate
|
arg1 > arg2
|
less
|
BinaryPredicate
|
arg1 < arg2
|
greater_equal
|
BinaryPredicate
|
arg1 >= arg2
|
less_equal
|
BinaryPredicate
|
arg1 <= arg2
|
logical_and
|
BinaryPredicate
|
arg1 && arg2
|
Logical_or
|
BinaryPredicate
|
arg1 || arg2
|
logical_not
|
UnaryPredicate
|
!arg1
|
unary_negate
|
Unary Logical
|
!(UnaryPredicate(arg1))
|
binary_negate
|
Binary Logical
|
!(BinaryPredicate(arg1, arg2))
|
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |