Noeud:Function Adaptors, Noeud « Next »:Introducing Generic Algorithms, Noeud « Previous »:Some Predefined Function Objects, Noeud « Up »:Generic Algorithms and Function Objects
Function adpators are function objects that enable us to pass in other
function objects as arguments, and there are a few worth mentioning that are
extremely useful. The first set enable us to bind different arguments
passed in (bind1st
and bind2nd
) to operations; the second set
let us pass member functions as arguments (mem_fun
and
mem_fun_ref
). Let's look at them each in turn.
Recall from Function Objects - in a Nutshell in function2.cc
we
defined our own with_each
method that took an additional parameter of
type T
so that we could pass in the value to make a comparison against
the elements of the container, using the greater
function object. The
reason we did this was because greater
is a binary function object, and
it needs a second value to compare against, and so we provided it by supplying
it a parameter. But the only way of doing this would be to modify the
with_each
method to cope with the extra parameter, which is what we
did. But wait - look what happens if we change with_each
as follows:
/* function3.cc * Compiled using g++ function3.cc -o function3 */ #include <vector> #include <functional> /* Define our own method that uses a unary function 'fn' on * a range of elements: */ template <class InputIterator, class UnaryFn, class Message> void with_each(InputIterator beg, InputIterator end, UnaryFn fn, Message msg) { for( ; beg != end; ++beg) if (fn(*beg)) cout << msg << *beg << endl; } int main() { std::vector<int> v; v.push_back(10); v.push_back(2); v.push_back(14); with_each(v.begin(), v.end(), bind1st(greater<int>(), 7), "7 is greater than "); return 0; }
Example 3.17: function3.cc
This may seem esoteric, but what's actually happening is bind1st
is
transforming greater
into a unary function object. The effect of
bind1st(op, val)
is to turn op
into a unary function object such
that op
will work with the parameters op(val, param)
. Thus,
val
will take on the value 7, and param
will be whatever value
we're working with inside the body of the with_each
method, in other
words the value held in *beg
. Thus, when fn(*beg)
is called in
the body of with_each
, we're calling greater
with one argument
(because bind1st
turned it into a unary function), and the condition
if (fn(*beg))
yields true for all values that are greater than 7.
bind2nd
is similar except that it binds its second parameter to be
the first argument to be used in function fn
. In other words, it
transforms bind2nd(op, val)
into op(param, val)
.
Lets now look at mem_fun_ref
and mem_fun
.
The easiest way to describe mem_fun_ref
is to revisit the
Address
class. First, recall from Vector how we printed
Address
objects:
/* Declare an iterator to work with: */ std::vector<Address>::iterator pos; /* Loop through the vector printing out elements: */ cout << "First iteration" << endl; for (pos=v.begin(); pos<v.end(); ++pos) { pos->print(); }
There's an easier way to do this, using mem_fun_ref
. In the following
code, we're actually going to use with_each
again, which may seem
counter-intuitive; well, it is if every time we're going to make function
object calls to iterate a collection of objects we have to define code to do
so. with_each
is just a stub for a generic algorithm called
for_each
, which we'll be looking at soon. Let's pass print
to
mem_fun_ref
as we traverse through a vector of Address
objects:
/* function4.cc Compiled using g++ function4.cc Address.cc -o function4 */ #include <functional> #include <vector> #include "AddressRepository.hh" /* Define our own method that uses a unary function 'fn' on * a range of elements: */ template <class InputIterator, class UnaryFn> void with_each(InputIterator beg, InputIterator end, UnaryFn fn) { for( ; beg != end; ++beg) fn(*beg); } int main() { vector<Address> v; /* Add all of the address objects to the vector: */ v.push_back(addr1); v.push_back(addr2); v.push_back(addr3); v.push_back(addr4); v.push_back(addr5); /* Now call 'print' with each element, passing it by reference: */ with_each(v.begin(), v.end(), mem_fun_ref(&Address::print)); exit(0); }
Example 3.18: function4.cc
The output is obvious: it prints out the list of Address
objects. This
is an extremely useful function object, because otherwise we'd have to define
our own function object (called fun_ob_print_address
within the
Address
class for example) which would do this work for us with the
with_each
algorithm, if we weren't happy with the pos->print()
way of doing things. This adds extra code and is uneccessary if we can use
mem_fun_ref
.
mem_fun
isn't too different; but instead of using a reference,
mem_fun
uses a pointer to an element.
Note that with both mem_fun
and mem_fun_ref
, the called member
functions must be constant member function, otherwise a compile error will
result.
These function adaptors are summarised in Function Adaptor Reference.