Chapter 8. STL Function Objects

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.

The Concept of 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:

  1. 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.

  2. 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.

  3. 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.

Function Objects as Sorting Criteria

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.

Function Objects with Internal State

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:

  1. You can pass the function objects by reference.

  2. 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 Return Value of for_each()

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 versus Function Objects

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.

Predefined Function Objects

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 1param2
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.

Function Adapters

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.

Function Adapters for Member Functions

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.

Function Adapters for Ordinary Functions

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.

User-Defined Function Objects for Function Adapters

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.

Supplementary Composing Function Objects

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.

Unary Compose Function Object Adapters

This subsection describes the most fundamental compose function object adapters. They are also part of SGI's STL implementation.

Nested Computations by Using compose_f_gx

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

Combining Two Criteria by Using compose_f_gx_hx

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

Binary Compose Function Object Adapters

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.200.242