22.4 Class Type Functors

Ru-Brd

Although pointers to functions are functors directly available in the language, there are many situations in which it is advantageous to use a class type object with an overloaded function call operator. Doing so can lead to added flexibility, added performance, or both.

22.4.1 A First Example of Class Type Functors

Here is a very simple example of a class type functor:

  // functors/functor1.cpp  #include <iostream>  // class for function objects that return constant value  class ConstantIntFunctor {    private:      int value;  // value to return on ''function call''  public:  // constructor: initialize value to return  ConstantIntFunctor (int c) : value(c) {      }  // ''function call''  int operator() () const {          return value;      }  };  // client function that uses the function object  void client (ConstantIntFunctor const& cif)  {      std::cout << "calling back functor yields " << cif() << '\n';  }  int main()  {      ConstantIntFunctor seven(7);      ConstantIntFunctor fortytwo(42);      client(seven);      client(fortytwo);  } 

ConstantIntFunctor is a class type from which functors can be generated. That is, if you create an object with

 ConstantIntFunctor seven(7);  // create function object  

the expression

 seven();  // call operator  ()  for function object  

is a call of operator () for the object seven rather than a call of function seven() . We achieve the same effect (indirectly) when passing the function objects seven and fortytwo through parameter cif to client() .

This example illustrates what is in practice perhaps the most important advantage of class type functors over pointers to functions: the ability to associate some state (data) with the function. This is a fundamental improvement in capabilities for callback mechanisms. We can have multiple "instances" of a function with behavior that is (in a sense) parameterized.

22.4.2 Type of Class Type Functors

There is more to class type functors than the addition of state information, however. In fact, if a class type functor does not encapsulate any state, its behavior is entirely subsumed by its type, and it is sufficient to pass the type as a template argument to customize a library component's behavior.

A classic illustration of this special case includes container classes that maintain their elements in some sorted order. The sorting criterion becomes a template argument, and because it is part of the container's type, accidental mixing of containers with different sorting criteria (for example, in an assignment) is caught by the type system.

The set and map containers of the C++ standard library are parameterized this way. For example, if we define two different sets using the same element type, Person , but different sorting criteria, a comparison of the sets results in a compile-time error:

 #include <set>  class Person {   };  class PersonSortCriterion {    public:      bool operator() (Person const& p1, Person const& p2) const {  // returns whether  p1  is ''less than''  p2   }  };  void foo()  {      std::set<Person, std::less<Person> > c0, c1;  // sort with operator  <      std::set<Person, std::greater<Person> > c2;  // sort with operator  >      std::set<Person, PersonSortCriterion> c3;  // sort with user-   // defined criterion  c0 = c1;  // OK: identical types  c1 = c2;  // ERROR: different types    if (c1 == c3) {  // ERROR: different types    }  } 

For all three declarations of a set , the element type and the sorting criterion are passed as template arguments. The standard function object type template std::less is defined to return the result of operator < as a result of a "function call." The following simplified implementation of std::less clarifies the idea [5] :

[5] The exact implementation differs because it is derived from a class std::binary_function . See Section 8.2.4 of [JosuttisStdLib] for details.

 namespace std {      template <typename T>      class less {        public:          bool operator() (T const& x, T const& y) const {              returnx<y;          }      };  } 

The std::greater template is similar.

Because all three sorting criteria have different types, the resulting sets also have different types. Therefore, any attempt to assign or to compare two of these sets fails at compile time (the comparison operator requires the same type). This may seem straightforward, but prior to templates, the sorting criterion might have been maintained as a function pointer field of the container. Any mismatch would likely not have been detected until run time (and perhaps not without much frustrating detective work).

Ru-Brd


C++ Templates
C++ Templates: The Complete Guide
ISBN: 0201734842
EAN: 2147483647
Year: 2002
Pages: 185

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net