Friday, May 4, 2012

predefined function objects c++


predefined function objects c++

The C++ standard library contains several predefined function objects that cover fundamental operations. By using them, you don't have to write your own function objects in several cases. A typical example is a function object used as a sorting criterion. The default sorting criterion for operator < is the predefined sorting criterion less<>. Thus, if you declare

set<int> coll;

it is expanded to

set<int, less<int> > coll; //sort elements with <

From there, it is easy to sort elements in the opposite order:

Note that you have to put a space between the two ">" characters. ">>" would be parsed as shift operator, which would result in a syntax error.

set<int ,greater<int> > coll; //sort elements with >

Similarly, many function objects are provided to specify numeric processing. For example, the following statement negates all elements of a collection:

transform (coll.begin() , coll.end(), //source
coll.begin(), //destination
negate<int>()) ; //operation

The expression creates a function object of the predefined template class negate that simply returns the negate element of type int for which it is called. The transform() algorithm uses that operation to transform all elements of the first collection into the second collection. If source and destination are equal (as in this case), the returned negated elements overwrite themselves. Thus, the statement negates each element in the collection.

Similarly, you can process the square of all elements in a collection:

//process the square of all elements
transform (coll.begin(), coll.end(), //first source
coll.begin(), //second source
coll.begin(), //destination
multiplies<int>()) ; //operation

Here, another form of the transform() algorithm combines elements of two collections by using the specified operation, and writes the resulting elements into the third collection. Again, all collections are the same, so each element gets multiplied by itself, and the result overwrites the old value.

Note: 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 operating system standards (X/Open, POSIX) and because multiplies was clearer.

By using special function adapters you can combine predefined function objects with other values or use special cases. Here is a complete example:


With the statement

transform (coll1.begin() ,coll1.end() //source
back_inserter (coll2) , //destination
bind2nd(multiplies<int>() ,10)) ; //operation

all elements of coll1 are transformed into coll2 (inserting) while multiplying each element by
10. Here, the function adapter bind2nd causes multiply<int> to be called for each element of the source collection as the first argument and the value 10 as the second.

The way bind2nd operates is as follows: transform() is expecting as its fourth argument an operation that takes one argument; namely, the actual element. However, we would like to multiply that argument by ten. So, we have to combine an operation that takes two arguments and the value that always should be used as a second argument to get an operation for one argument. bind2nd does that job. It stores the operation and the second argument as internal values. When the algorithm calls bind2nd with the actual element as the argument, bind2nd calls its operation with the element from the algorithm as the first argument and the internal value as the second argument, and returns the result of the operation.

Similarly, in

replace_if (coll2.begin(),coll2.end(), //range
bind2nd(equal_to<int>(),70), //replace criterion
42);

the expression

bind2nd(equal_to<int>(),70)

is used as a criterion to specify the elements that are replaced by 42. bind2nd calls the binary
predicate equal_to with value 70 as the second argument, thus defining a unary predicate for
the elements of the processed collection. The last statement is similar because the expression

bind2nd(less<int>(),50)

elements that are less than value 50 be removed. The output of the program is as follows:

initialized: 9 8 7 6 5 4 3 2 1
transformed: 90 80 70 60 50 40 30 20 10
replaced: 90 80 42 60 50 40 30 20 10
removed: 90 80 60 50

This kind of programming results in functional composition. What is interesting is that all these function objects are usually declared inline. Thus, you use a function-like notation or abstraction, but you get good performance.

There are other kinds of function objects. For example, some function objects provide the ability to call a member function for each element of a collection:

for_each (coll.begin(), coll.end(), //range
mem_fun_ref (&Person: : save)); //operation

The function object mem_fun_ref calls a specified member function for the element for which it is called. Thus, for each element of the collection coll, the member function save() of class Person is called. Of course, this works only if the elements have type Person or a type derived from Person.

No comments:

Post a Comment