This chapter discusses in detail function objects, or functors for short, which were introduced in Section 5.9. It covers the full set of predefined function objects and function adapters, and the concept of functional composition, and provides examples of self-written function objects.
A function object (or functor), is an object that has operator ()
defined so that in the following example
FunctionObjectType fo;
...
fo(...);
the expression fo()
is a call of operator ()
for the function object fo
instead of a call of the function fo().
At first, you could consider a function object as an ordinary function that is written in a more complicated way: Instead of writing all the function statements inside the function body,
void fo() {
statements
}
you write them inside the body of operator ()
of the function object class:
class FunctionObjectType { public: void operator() () { statements } };
This kind of definition is more complicated; however, it has three important advantages:
A function object might be smarter because it may have a state. In fact, you can have two instances of the same function, represented by a function object, which may have different states at the same time. This is not possible for ordinary functions.
Each function object has its own type. Thus, you can pass the type of a function object to a template to specify a certain behavior, and you have the advantage that container types with different function objects differ.
A function object is usually faster than a function pointer.
See page 126 for more details about these advantages and page 127 for an example that shows how function objects can be smarter than ordinary functions.
In the next two subsections I present two other examples that go into more detail about function objects. The first example demonstrates how to benefit from the fact that each function object usually has its own type. The second example demonstrates how to benefit from the state of function objects, and leads to an interesting property of the for_each()
algorithm, which is covered in another subsection.
Programmers often need a sorted collection of elements that have a special class (for example, a collection of persons
). However, you either don't want to use or you can't use the usual operator <
to sort the objects. Instead, you sort the objects according to a special sorting criterion based on some member function. In this regard, a function object can help. Consider the following example:
// fo/sortl.cpp #include <iostream> #include <string> #include <set> #include <algorithm> using namespace std; class Person { public: string firstname() const; string lastname() const; ... }; /* class for function predicate * - operator() returns whether a person is less than another person */ class PersonSortCriterion { public: bool operator() (const Person& p1, const Person& p2) const { /* a person is less than another person * - if the last name is less * - if the last name is equal and the first name is less */ return p1.lastname()<p2.lastname() || (! (p2.lastname()<p1.lastname()) && p1.firstname()<p2.firstname()); } }; int main() { //declare set type with special sorting criterion typedef set<Person,PersonSortCriterion> PersonSet; //create such a collection PersonSet coll; ... //do something with the elements PersonSet::iterator pos; for (pos = coll.begin(); pos != coll.end();++pos) { ... } ... }
The set coll
uses the special sorting criterion PersonSortCriterion,
which is defined as a function object class. PersonSortCriterion
defines operator ()
in such a way that it compares two Persons
according to their last name and (if they are equal) to their first name. The constructor of coll
creates an instance of class PersonSortCriterion
automatically so that the elements are sorted according to this sorting criterion.
Note that the sorting criterion PersonSortCriterion
is a type. Thus, you can use it as a template argument for the set. This would not be possible, if you implement the sorting criterion as a plain function (as was done on page 123).
All sets with this sorting criterion have their own type (which is called PersonSet
in this example). You can't combine or assign a set that has a "normal" or another user-defined sorting criterion. Thus, you can't compromise the automatic sorting of the set by any operation; however, you can design function objects that represent different sorting criteria with the same type (see the next subsection). See page 178 for more details about sets and their sorting criteria.
The following example shows how function objects can be used to behave as a function that may have more than one state at the same time:
// fo/genera1.cpp #include <iostream> #include <list> #include <algorithm> #include "print.hpp" using namespace std; class IntSequence { private: int value; public: //constructor IntSequence (int initialValue) : value(initialValue) { } //''function call'' int operator() () { return value++; } }; int main() { list<int> coll; //insert values from 1 to 9 generate_n (back_inserter(coll), //start 9, //number of elements IntSequence (1)); //generates values PRINT_ELEMENTS(coll); //replace second to last element but one with values starting at 42 generate (++coll.begin(), //start --coll.end(), //end IntSequence (42)); //generates values PRINT_ELEMENTS(coll); }
In this example, a function object is used that generates a sequence of integral values. Each time operator ()
is called, it returns its actual value and increments it. You can pass the start value as a constructor argument.
Two such function objects are then used by the generate()
and generate_n()
algorithms. These algorithms use generated values to write them into a collection: The expression
IntSequence(1)
in the statement
generate_n (back_inserter(coll), 9, IntSequence(1));
creates such a function object initialized with 1.
The generate_n()
algorithm uses it nine times to write an element, so it generates values 1
to 9
Similarly, the expression
IntSequence(42)
generates a sequence beginning with value 42.
The generate()
algorithm replaces the elements beginning with ++coll.begin()
up to −−coll.end().
[1] The output of the program is as follows:
1 2 3 4 5 6 7 8 9 1 42 43 44 45 46 47 48 9
Using other versions of operator (),
you can produce more complicated sequences easily.
Function objects are passed by value rather than by reference. Thus, the algorithm does not change the state of the function object. For example, the following code generates the sequence starting with value 1
twice:
IntSequence seq(1); //integral sequence starting with value 1 //insert sequence beginning with 1 generate_n (back_inserter(coll), 9, seq); //insert sequence beginning with 1 again generate_n (back_inserter(coll), 9, seq);
Passing function objects by value instead of by reference has the advantage that you can pass constant and temporary expressions. Otherwise, passing IntSequence(1)
would not be possible.
The disadvantage of passing the function object by value is that you can't benefit from modifications of the state of the function objects. Algorithms can modify the state of the function objects, but you can't access and process their final states because they make internal copies of the function objects. However, access to the final state might be necessary, so the question is how to get a "result" from an algorithm.
There are two ways to get a "result" or "feedback" from using function objects with algorithms:
You can pass the function objects by reference.
You can use the return value of the for_each()
algorithm.
The latter is discussed in the next subsection.
To pass a function object by reference you simply have to qualify the call of the algorithm so that the function object type is a reference.[2] For example:
// fo/genera2.cpp #include <iostream> #include <list> #include <algorithm> #include "print.hpp" using namespace std; class IntSequence { private: int value; public: //constructor IntSequence (int initialValue) : value(initialValue) { } // "function call" int operator() () { return value++; } }; int main() { list<int> coll; IntSequence seq(1); //integral sequence starting with 1 //insert values from 1 to 4 // - pass function object by reference //so that it will continue with 5 generate_n<back_insert_iterator<list<int> >, int, IntSequence&>(back_inserter(coll), //start 4, //number of elements seq); //generates values PRINT_ELEMENTS(coll); //insert values from 42 to 45 generate_n (back_inserter(coll), //start 4, //number of elements IntSequence(42)) ; //generates values PRINT_ELEMENTS(coll); //continue with first sequence // - pass function object by value //so that it will continue with 5 again generate_n (back_inserter(coll), //start 4, //number of elements seq) ; //generates values PRINT_ELEMENTS(coll); //continue with first sequence again generate_n (back_inserter(coll), //start 4, //number of elements seq); //generates values PRINT_ELEMENTS(coll); }
The program has the following output:
1 2 3 4 1 2 3 4 42 43 44 45 1 2 3 4 42 43 44 45 5 6 7 8 1 2 3 4 42 43 44 45 5 6 7 8 5 6 7 8
In the first call of generate_n()
the function object seq
is passed by reference. To do this, the template arguments are qualified explicitly:
generate_n<back_insert_iterator<list<int> >, int, IntSequence&>(back_inserter(coll), //start 4, //number of elements seq); //generates values
As a result, the internal value of seq
is modified after the call and the second use of seq
by the third call of generate_n()
continues the sequence of the first call. However, this call passes seq
by value:
generate_n (back_inserter(coll), //start 4, //number of elements seq); //generates values
Thus, the call does not change the state of seq.
As a result, the last call of generate_n()
continues the sequence with value 5
again.
The effort involved with a reference-counted implementation of a function object to access its final state is not necessary if you use the for_each()
algorithm. for_each()
has the unique ability to return its function object (no other algorithm can do this). Thus you can query the state of your function object by checking the return value of for_each().
The following program is a nice example of the use of the return value of for_each().
It shows how to process the mean value of a sequence:
//fo/foreach3.cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; //function object to process the mean value class MeanValue { private: long num; //number of elements long sum; //sum of all element values public: //constructor MeanValue() : num(0), sum(0) { } //"function call" //-process one more element of the sequence void operator() (int elem) { num++; //increment count sum += elem; //add value } //return mean value double value () { return static_cast<double>(sum) / static_cast<double>(num); } }; int main() { vector<int> coll; //insert elments from 1 to 8 for (int i=1; i<=8; ++i) { coll.push_back(i); } //process and print mean value MeanValue mv = for_each (coll.begin(), coll.end(), //range MeanValue()); //operation cout << "mean value: " << mv.value() << endl; }
The expression
MeanValue()
creates a function object that counts the number of elements and processes the sum of all element values. By passing it to for_each(),
it is called for each element of the container coll:
MeanValue mv = for_each (coll.begin(), coll.end(), MeanValue());
The function object is returned and assigned to mv,
so you can query its state after the statement by calling: mv.value().
Therefore, the program has the following output:
mean value: 4.5
You could even make the class MeanValue
a bit smarter by defining an automatic type conversion to double.
Then you could use the mean value that is processed by for_each()
directly. See page 336 for such an example.
Predicates are functions or function objects that return a Boolean value (a value that is convertible to bool
). However, not every function that returns a Boolean value is a valid predicate for the STL. This may lead to surprising behavior. Consider the following example:
// fo/removeif.cpp #include <iostream> #include <list> #include <algorithm> #include "print.hpp" using namespace std; class Nth { //function object that returns true for the nth call private: int nth; //call for which to return true int count; //call counter public: Nth (int n) : nth (n), count (0) { } bool operator() (int) { return ++count == nth; } }; int main() { list<int> coll; //insert elements from 1 to 9 for (int i=1; i<=9; ++i) { coll.push_back(i); } PRINT_ELEMENTS(coll,"coll: "); //remove third element list<int>::iterator pos; pos = remove_if (coll.begin(),coll.end(), //range Nth(3)); //remove criterion coll.erase (pos,coll.end()); PRINT_ELEMENTS (coll, "nth removed: "); }
This program defines a function object Nth
that yields true
for the nth call. However, when passing it to remove_if()
(an algorithm that removes all elements for which a unary predicate yields true,
see page 378), the result is a big surprise:
coll: 1 2 3 4 5 6 7 8 9 nth removed: 1 2 4 5 7 8 9
Two elements, namely the third and sixth elements are removed. This happens because the usual implementation of the algorithm copies the predicate internally during the algorithm:
template <class ForwIter, class Predicate> ForwIter std::remove_if(ForwIter beg, ForwIter end, Predicate op) { beg = find_if(beg, end, op); if (beg == end) { return beg; } else { ForwIter next = beg; return remove_copy_if(++next, end, beg, op); } }
The algorithm uses find_if()
to find the first element that should be removed. However, it then uses a copy of the passed predicate op
to process the remaining elements if any. Here, Nth
in its original state is used again and it also removes the third element of the remaining elements, which is in fact the sixth element.
This behavior is not a bug. The standard does not specify how often a predicate might be copied internally by an algorithm. Thus, to get the guaranteed behavior of the C++ standard library you should not pass a function object for which the behavior depends on how often it is copied or called. Thus, if you call a unary predicate for two arguments and both arguments are equal, then the predicate should always yield the same result. That is, a predicate should not change its state due to a call, and a copy of a predicate should have the same state as the original. To ensure that you can't change the state of a predicate due to a function call, you should declare operator ()
as constant member function.
It is possible to avoid this surprising behavior and to guarantee that this algorithm works as expected even for a function object such as Nth,
without any performance penalties. You could implement remove_if()
in such a way that the call of find_if()
is replaced by its contents:
template <class ForwIter, class Predicate> ForwIter std::remove_if(ForwIter beg, ForwIter end, Predicate op) { while (beg != end && !op(*beg)) { ++beg; } if (beg == end) { return beg; } else { ForwIter next = beg; return remove_copy_if(++next, end, beg, op); } }
So, it might be a good idea to change the implementation of remove_if()
(or submit a change request to the implementor of the library). To my knowledge, in current implementations this problem only arises with the remove_if()
algorithm. If you use remove_copy_if(),
all works as expected.[3] However, to be portable, you should never rely on this implementation detail. You should always declare the function call operator of predicates as being a constant member function.
As mentioned in Section 5.9.2, the C++ standard library provides many predefined function objects. Table 8.1 lists all predefined function objects.[4]
Table 8.1. Predefined Function Objects
Expression | Effect |
---|---|
negate< type>()
| −param |
plus< type>()
| param1
+
param2
|
minus< type>()
| param 1 − param2 |
multiplies< type>() [4]
| param1
* param2
|
divides< type>()
| param1
/ param2
|
modulus < type>()
| param1
% param2
|
equal_to< type>()
| param1
== param2
|
not_equal_to< type>()
| param1
! = param2
|
less< type>()
| param1
< param2
|
greater< type>()
| param1
> param2
|
less_equal< type>()
| param1
<= param2
|
greater_equal< type>()
| param1
>= param2
|
logical_not< type>()
| ! param
|
logical_and< type>()
| param1
&& param2
|
logical_or< type> ()
| param1
| | param2
|
less<>
is the default criterion whenever objects are sorted or compared, so it is used often. Default sorting operations always produce an ascending order (element
<
nextElement). To use these function objects, you must include the header file <functional>
[5]:
#include <functional>
To compare internationalized strings, the C++ standard library provides another function object that can be used as a sorting criterion for strings. See page 703 for details.
A function adapter is a function object that enables the combining of function objects with each other, with certain values, or with special functions. Function adapters are also declared in <functional>.
For example, in the following statement:
find_if (coll.begin(), coll.end(), //range bind2nd (greater<int>(), 42)) //criterion
the expression
bind2nd(greater<int>(),42)
produces a combined function object that checks whether an int
value is greater than 42.
In fact, bind2nd
transforms a binary function object, such as greater<>,
into a unary function object. It always uses its second parameter as the second argument of the binary function object that is passed as the first parameter. Thus, in this example it always calls greater<>
with 42
as the second argument. Section 5.9.2, offers some other examples of the use of function adapters.
Table 8.2 lists the predefined function adapter classes provided by the C++ standard library.
Table 8.2. Predefined Function Adapters
Expression | Effect |
---|---|
bind1st
(op,value)
| op(value,param) |
bind2nd
(op, value)
| op(param,value) |
not 1 (op)
| !op(param) |
not2 (op)
| !op(param1 ,param2) |
Function adapters are function objects themselves, so you can combine function adapters and function objects to form more powerful (and more complicated) expressions. For example, the following statement returns the first even element of a collection:
pos = find_if (coll.begin(), coll.end(), //range not1 (bind2nd(modulus<int>(),2))); //criterion
In this statement, the expression
bind2nd(modulus<int>(),2)
returns 1
for all odd values. So this expression as a criterion finds the first element that has an odd value because 1
is equivalent to true not1()
negates the result, so the whole statement searches for the first element that has an even value.
By using function adapters you can combine different function objects to form very powerful expressions. This kind of programming is called functional composition. However, the C++ standard library lacks some function adapters that are necessary and useful for functional composition. For example, some function adapters are missing that allow you to combine two predicates with "and" or "or" (such as, "greater than 4 and less than 7"). If you extend the standard function adapters by some composing function adapters you get a lot more power. See Section 8.3, for a description of such extensions.
The C++ standard library provides some additional function adapters that enable you to call a member function for each element of a collection (Table 8.3).
Table 8.3. Function Adapters for Member Functions
Expression | Effect |
---|---|
mem_fun_ref
(op)
| Calls op() as a member function for an object |
mem_fun
(op)
| Calls op() as a member function for an object |
For example, in the following code mem_fun_ref
is used to call a member function for objects of a vector:
// fo/memfunla.cpp class Person { private: std::string name; public: ... void print() const { std::cout << name << std::endl; } void printWithPrefix (std::string prefix) const { std::cout << prefix << name << std::endl; } }; void foo (const std::vector<Person>& coll) { using std::for_each; using std::bind2nd; using std::mem_fun_ref; //call member function print() for each element for_each (coll.begin(), coll.end(), mem_fun_ref(&Person::print)); //call member function printWithPrefix() for each element //-"person: " is passed as an argument to the member function for_each (coll.begin(), coll.end(), bind2nd (mem_fun_ref (&Person::printWithPrefix), "person: ")); }
In foo(),
two different member functions of class Person
are called for each element in the vector coll:
(1)Person::print(),
which has no parameter, and (2)Person::printWithPrefix(),
which has an additional parameter. To call the Person::print()
member function, the function object
mem_fun_ref (&Person::print)
is passed to the for_each()
algorithm:
for_each (coll.begin(), coll.end(), mem_fun_ref(&Person::print));
The mem_fun_ref
adapter transforms the function call for the element into a call of the passed member function.
The adapter is necessary because you can't pass a member function directly to an algorithm. Doing so would cause a compile-time error:
for_each (coll.begin(), coll.end (), &Person:: print); //ERROR: can't call operator() // for a member function pointer
The problem is that for_each()
would call operator()
for the pointer passed as the third argument instead of calling the member function to which it points. The mem_fun_ref
adapter solves this problem by transforming the call of operator().
By using bind2nd
it is also possible to pass one argument to the called member function, as the second call of for_each()
shows[6]:
for_each (coll.begin(), coll.end(), bind2nd(mem_fun_ref (&Person::printWithPrefix), "person: "));
You might wonder why the adapter is called mem_fun_ref
instead of simply mem_fun.
The reason is historical: Another version of member function adapters was introduced first and got the name mem_fun.
Those mem_fun
adapters are for sequences that contain pointers to elements. Probably mem_fun_ptr
would have been a less confusing name for them. So, if you have a sequence of pointers to objects, you can also call member functions for them. For example:
// fo/memfun1b.cpp void ptrfoo (const std::vector<Person*>& coll) //^^^ pointer! { using std::for_each; using std::bind2nd; using std::mem_fun; //call member function print() for each referred object for_each (coll.begin() , coll.end(), mem_fun(&Person::print)); //call member function printWithPrefix()for each referred object //-"person: " is passed as an argument to the member function for_each (coll.begin() , coll.end(), bind2nd(mem_fun(&Person::printWithPrefix), "person: ")); }
Both mem_fun_ref
and mem_fun
can call member functions with zero or one argument. However, you can't call member functions with two or more arguments in this way. This is because for the implementation of these adapters you need auxiliary function objects that are provided for each kind of member function. For example, the auxiliary classes for mem_fun
and mem_fun_ref
are mem_fun_t, mem_fun_ref_t, const_mem_fun_t, const_mem_fun_ref_t, mem_fun1_t, mem_fun1_ref_t, const_mem_fun1_t,
and const_mem_fun1_ref_t.
Note that the member functions called by mem_fun_ref
and mem_fun
must be constant member functions. Unfortunately, the C++ standard library does not adapters for nonconstant member functions (I discovered this while writing this book). It seems to have been simply an oversight because nobody knew that this was not possible, and it is possible to solve this problem without much effort. Hopefully, implementations (and the standard) will fix this problem in the future.
Another function adapter enables ordinary functions to be used from other function adapters: ptr_fun
(Table 8.4).
For example, suppose you have a global function such as the following that checks something for each parameter:
bool check(int elem);
Table 8.4. Functions Adapters for Ordinary Functions
Expression | Effect |
---|---|
ptr_fun (op)
| *op(param) |
*op(param1, param2) |
If you want to find the first element for which the check does not succeed you could call the following statement:
pos = find_if (coll.begin(), coll.end(), //range not1(ptr_fun(check))); //search criterion
You could not use not1(check)
because not1()
uses special type members that function objects provide. See Section 8.2.4 for more details.
The second form is used when you have a global function for two parameters and, for example, you want to use it as a unary function:
//find first string that is not empty pos = find_if (coll.begin(), coll.end(), //range bind2nd(ptr_fun(strcmp),"")); //search criterion
Here, the strcmp()
C function is used to compare each element with the empty C-string. strcmp()
returns 0,
which is equivalent to false,
when both strings match. So, this call of find_if()
returns the position of the first element that is not the empty string. See another example of the use of ptr_fun
on page 319.
You can write your own function objects, but to use them in combination with function adapters they must meet certain requirements: They must provide type members for the type of their arguments and the result. The C++ standard library provides structures to make this more convenient:
template <class Arg, class Result> struct unary_function { typedef Arg argument_type; typedef Result result_type; }; template <class Argl, class Arg2, class Result> struct binary_function { typedef Argl first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; };
Thus, by deriving your function object from one of these types you meet the requirements easily so that your function object becomes "adapter-able."
The following example shows a complete definition for a function object that processes the first argument raised to the power of the second argument:
// fo/fopow.hpp
#include <functional>
#include <cmath>
template <class T1, class T2>
struct fopow : public std::binary_function<T1, T2, T1>
{
T1 operator() (T1 base, T2 exp) const {
return std::pow(base,exp);
}
};
Here, the first argument and the return value have the same type, T1,
and the exponent may have a different type T2.
These types are passed to binary_function
to make the required type definitions. However, instead of passing them to binary_function
you could make the type definition directly. As usual in the STL, the concept of function adapters is pure abstraction: Anything that behaves like a function object for function adapters is a function object for function adapters.
The following program shows how to use the user-defined function object fopow.
In particular, it uses fopow
with the bind1st
and bind2nd
function adapters:
// fo/fopow1. cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; //include self-defined fopow<> #include "fopow.hpp" int main() { vector<int> coll; //insert elements from 1 to 9 for (int i=1; i<=9; ++i) { coll.push_back(i); } //print 3 raised to the power of all elements transform (coll.begin(), coll.end(), //source ostream_iterator<int>(cout," "), //destination bind1st(fopow<float, int>() ,3)); //operation cout << endl; //print all elements raised to the power of 3 transform (coll.begin(), coll.end(), //source ostream_iterator<int> (cout," "), //destination bind2nd(fopow<float,int>(),3)) ; //operation cout << endl; }
The program has the following output:
3 9 27 81 243 729 2187 6561 19683 1 8 27 64 125 216 343 512 729
Note that fopow
is realized for types float
and int.
If you use int
for both base and exponent, you'd call pow()
with two arguments of type int,
but this isn't portable because according to the standard pow()
is overloaded for more than one but not all fundamental types:
transform (coll.begin(), coll.end(),
ostream_iterator<int>(cout," "),
bind1st(fopow<int,int>() ,3)); //ERROR:ambiguous
See page 581 for details about this problem.
The ability to compose function objects is important for building software components from other components. It enables you to construct very complicated function objects from simple ones. So in general it should be possible to define almost every functional behavior as a combination of function objects. However, the C++ standard library does not provide enough adapters to support this. For example, it is not possible to combine the result of two unary operations to formulate a criterion such as "this and that."
In principal, the following compose adapters are useful:
f (g(
elem))
This is the general form of a unary compose function. It allows nested calls of unary predicates such that the result of calling predicate g()
for elem is used as input for predicate f().
The whole expression operates as a unary predicate.
f (g(
elem1,elem2))
This is a form in which two elements, elem1 and elem2, are passed as arguments to a binary predicate g().
Again the result is used as input for the unary predicate f().
The whole expression operates as a binary predicate.
f (g(
elem),h(
elem))
This is a form in which elem is passed as an argument to two different unary predicates g()
and h(),
and the result of both is processed by the binary predicate f().
In a way, this form "injects" a single argument into a composed function. The whole expression operates as a unary predicate.
f (g(
elem1) ,h(
elem2))
This is a form in which two elements, elem1 and elem2, are passed as an argument to two different unary predicates g()
and h(),
and the result of both is processed by the binary predicate f().
In a way, this form "distributes" a composed function over two arguments. The whole expression operates as a binary predicate.
Unfortunately, these compose adapters were not standardized, so we don't have standard names for them. SGI's implementation of the STL has names for two of them, however the community is currently looking for general names for all these adapters. See Table 8.5 for some possible names and the names I chose to use in this book.
Table 8.5. Possible Names of Compose Function Object Adapters
Functionality | This Book | SGI STL |
---|---|---|
f (g (elem))
| compose_f_gx
| compose1
|
f (g (elem1,elem2))
| compose_f_gxy
| |
f (g (elem),h (elem))
| compose_f_gx_hx
| compose2
|
f (g (elem1),h (elem2))
| compose_f_gx_hy
|
Look at the Boost repository for C++ libraries at http://www.boost.org/ for the names that should be used in the future and for a complete implementation of all of them. In the next few subsections I discuss three of them—those that I need most often.
This subsection describes the most fundamental compose function object adapters. They are also part of SGI's STL implementation.
The simplest and most fundamental compose function adapter uses the result of a unary operation as input to another unary operation. Thus, it is simply a nested call of two unary function objects. You need this function adapter to formulate something like "add 10 and multiply by 4."
I use the name compose_f_gx
for this function object adapter. SGI's implementation of the STL uses the name compose1.
You can implement compose_f_gx
as follows:
// fo/compose11.hpp #include <functional> /* class for the compose_f_gx adapter */ template <class 0P1, class 0P2> class compose_f_gx_t : public std::unary_function<typename 0P2::argument_type, typename 0P1::result_type> { private: 0P1 op1; //process: op1(op2(x)) 0P2 op2; public: //constructor compose_f_gx_t(const 0P1& o1, const 0P2& o2) : 0p1(o1), op2(o2) { } //function call typename 0P1::result_type operator() (const typename 0P2::argument_type& x) const { return op1 (op2(x)); } }; /*convenience function for the compose _f_gx adapter */ template <class 0P1, class 0P2> inline compose_f_gx_t<0Pl,0P2> compose_f_gx (const 0P1& o1, const OP2& o2) { return compose_f_gx_t<0Pl,OP2>(ol,o2); }
Here is a complete example that demonstrates the use of compose_f_gx:
// fo/compose1. cpp #include <iostream> #include <vector> #include <algorithm> #include <functional> #include "print.hpp" #include "composell.hpp" using namespace std; int main() { vector<int> coll; //insert elements from 1 to 9 for (int i=1; i<=9; ++i) { coll.push_back(i); } PRINT_ELEMENTS(coll); //for each element add 10 and multiply by 5 transform (coll.begin(),coll.end(), ostream_iterator<int>(cout," "), compose_f_gx (bind2nd (multiplies<int>(),5), bind2nd (plus<int>(),10))); cout << endl; }
Note that the second operation passed to compose_f_gx
is performed first. Thus,
compose_f_gx(bind2nd(multiplies<int>(),5), bind2nd (plus<int>(),10))
yields a unary function object that first adds ten and then multiplies the result by five. The program has the following output:
1 2 3 4 5 6 7 8 9 55 60 65 70 75 80 85 90 95
Probably the most important supplementary function adapter is one that allows you to combine two criteria logically to formulate a single criterion. You need this function adapter to formulate something like "greater than 4 and less than 7."
I use the name compose_f_gx_hx
for this function object adapter. In SGI's implementation of the STL it is called compose2.
You can implement compose_f_gx_hx
as follows:
// fo/compose21.hpp #include <functional> /*class for the compose_f_gx_hx adapter */ template <class 0P1, class 0P2, class 0P3> class compose_f_gx_hx_t : public std::unary_function<typename 0P2::argument_type, typename 0P1::result_type> { private: 0P1 op1; //process: op1 (op2(x), op3(x)) 0P2 op2; 0P3 op3; public: //constructor compose_f_gx_hx_t (const 0P1& o1, const 0P2& o2, const 0P3& o3) : op1(o1), op2(o2), op3(o3) { } //function call typename 0P1::result_type operator()(const typename 0P2::argument_type& x) const { return op1(op2(x),op3(x)); } }; /*convenience function for the compose _f_gx_hx adapter */ template <class 0P1, class 0P2, class 0P3> inline compose_f_gx_hx_t<0Pl,0P2,0P3> compose_f_gx_hx (const 0P1& o1, const 0P2& o2, const 0P3& o3) { return compose_f_gx_hx_t<0Pl,0P2,0P3>(ol,o2,o3); }
compose_f_gx_hx
uses the first operation to combine the results of two unary operations for the same object. Thus, the expression
compose_f_gx_hx(opl,op2,op3)
results in the unary predicate that calls for each value x:
op1(op2(x),op3(x))
Here is a complete example that demonstrates the use of compose_f_gx_hx:
// fo/compose2.cpp #include <iostream> #include <vector> #include <algorithm> #include <functional> #include "print.hpp" #include "compose21.hpp" using namespace std; int main() { vector<int> coll; //insert elements from 1 to 9 for (int i=1; i<=9; ++i) { coll.push_back(i); } PRINT_ELEMENTS(coll); //remove all elements that are greater than four and less than seven // - retain new end vector<int>::iterator pos; pos = remove_if (coll.begin(),coll.end(), compose_f_gx_hx(logical_and<bool>(), bind2nd(greater<int>(),4), bind2nd(less<int>(),7))); //remove "removed" elements in coll coll.erase(pos,coll.end()); PRINT_ELEMENTS(coll); }
The expression
compose_f_gx_hx(logical_and<bool>(), bind2nd(greater<int>(),4), bind2nd(less<int>(),7))
yields a unary predicate that returns whether a value is greater than four and less than seven. The program has the following output:
1 2 3 4 5 6 7 8 9 1 2 3 4 7 8 9
One of the binary compose function object adapters processes the result of two unary operations that use different elements as parameters. I use the name compose_f_gx_hy
for this function object adapter. Here is a possible implementation:
// fo/compose22.hpp #include <functional> /*class for the compose_ f_gx_hy adapter */ template <class 0P1, class 0P2, class 0P3> class compose_f_gx_hy_t : public std::binary_function<typename 0P2::argument_type, typename 0P3::argument_type, typename 0P1::result_type> { private: 0P1 op1; //process: op1 (op2(x) ,op3(y)) 0P2 op2; 0P3 op3; public: //constructor compose_f_gx_hy_t (const 0P1& o1, const 0P2& o2, const 0P3& o3) : op1(o1), op2(o2), op3(o3) { } //function call typename 0P1::result_type operator()(const typename 0P2::argument_type& x, const typename 0P3::argument_type& y) const { return op1(op2(x),op3(y)); } }; /*convenience function for the compose _f_gx_hy adapter */ template <class 0P1, class OP2, class 0P3> inline composef_gx_hy_t<0Pl,0P2,0P3> compose_f_gx_hy (const 0P1& o1, const 0P2& o2, const 0P3& o3) { return compose_f_gx_hy_t<0Pl,0P2,0P3>(ol,o2,o3); }
The following example shows the use of compose_f _gx_hy.
It searches for a substring in a string in a case-insensitive way:
// fo/compose3.cpp #include <iostream> #include <algorithm> #include <functional> #include <string> #include "compose22.hpp" using namespace std; int main() { string s("Internationalization"); string sub("Nation"); //search substring case insensitive string::iterator pos; pos = search (s .begin(), s. end(), //string to search in sub.begin() ,sub.end() , //substring to search compose_f _gx_hy(equal_to<int>(), //compar. criterion ptr_fun::(toupper), ptr_fun::(toupper))); if (pos != s.end()) { cout << """ << sub << "" is part of "" << s << """ << end1; } }
The program has the following output:
"Nation" is part of "Internationalization"
On page 499 you will find an example program that searches a substring in a case-insensitive way without using compose_f_gx_hy.
[1] The expressions
++coll.begin()
and
−−coll.end()
might not work with vectors. This nasty problem is discussed in Section 7.2.6.
[2] Thanks to Philip Köster for pointing this out.
[3] Whether the C++ standard library should guarantee the expected behavior in cases such as those presented in this example is currently under discussion.
[4] In earlier versions of the STL, the function object for multiplication had the name times.
This was changed due to a name clash with a function of the operating system standards (X/Open, POSIX) and because multiplies
was clearer.
[5] In the original STL, the header file for function objects was called <function.h>.
[6] In older versions of the STL and the C++ standard library, the member function adapters for one argument were called mem_fun1
and mem_fun1_ref
instead of mem_fun
and mem_fun_ref.
18.191.200.242