16.5 Function Objects

As we’ve shown in this chapter, many Standard Library algorithms allow you to pass a lamda or a function pointer into the algorithm to help the algorithm perform its task. For example, the binary_search algorithm that we discussed in Section 16.4.6 is overloaded with a version that requires as its fourth parameter a function that takes two arguments and returns a bool value. The algorithm uses this function to compare the search key to an element in the collection. The function returns true if the search key and element being compared are equal; otherwise, the function returns false. This enables binary_search to search a collection of elements for which the element type does not provide an overloaded < operator.

Any algorithm that can receive a lambda or function pointer can also receive an object of a class that overloads the function-call operator (parentheses) with a function named operator(), provided that the overloaded operator meets the requirements of the algorithm—in the case of binary_search, it must receive two arguments and return a bool. An object of such a class is known as a function object and can be used syntactically and semantically like a lambda, a function or a function pointer—the overloaded parentheses operator is invoked by using a function object’s name followed by parentheses containing the arguments to the function. Most algorithms can use lambdas, function pointers and function objects interchangeably.2 In fact, lambdas are converted by the compiler into function pointers or function objects, which can be inlined by the compiler for optimization purposes.

Advantages of Function Objects over Function Pointers

Function objects provide several advantages over function pointers. The compiler can sometimes inline a function object’s overloaded operator() to improve performance. Also, since they’re objects of classes, function objects can have data members that operator() can use to perform its task.

Predefined Function Objects of the Standard Template Library

Many predefined function objects can be found in the header <functional>. Figure 16.14 lists several of the dozens of Standard Library function objects, which are all implemented as class templates—you can see the complete list at


http://en.cppreference.com/w/cpp/utility/functional

Section 20.9 of the C++ standard contains the complete list of function objects. We used the function object less<T> in the set and multiset examples to specify the sorting order for elements in a container. Recall that many of the overloaded Standard Library algorithms can receive as their last argument a binary function that determines whether its first argument is less than its second—exactly the purpose of the less<T> function object.

Fig. 16.14 Some function objects in the Standard Library.

Function object Type
divides<T> arithmetic
equal_to<T> relational
greater<T> relational
greater_equal<T> relational
less<T> relational
less_equal<T> relational
logical_and<T> logical
logical_not<T> logical
logical_or<T> logical
minus<T> arithmetic
modulus<T> arithmetic
negate<T> arithmetic
not_equal_to<T> relational
plus<T> arithmetic
multiplies<T> arithmetic

Using the accumulate Algorithm

Figure 16.15 uses the accumulate numeric algorithm (introduced in Fig. 16.6) to calculate the sum of the squares of the elements in an array. The fourth argument to accumulate is a binary function object (that is, a function object for which operator() takes two arguments) or a function pointer to a binary function (that is, a function that takes two arguments). Function accumulate is demonstrated three times—once with a function pointer, once with a function object and once with a lamdba.

Fig. 16.15 Demonstrating function objects.

Alternate View

 1   // Fig. 16.15: fig16_15.cpp
 2   // Demonstrating function objects.
 3   #include <iostream>
 4   #include <array> // array class-template definition
 5   #include <algorithm> // copy algorithm
 6   #include <numeric> // accumulate algorithm
 7   #include <functional> // binary_function definition
 8   #include <iterator> // ostream_iterator
 9   using namespace std;
10
11   // binary function adds square of its second argument and the  
12   // running total in its first argument, then returns the sum   
13   int sumSquares(int total, int value) {                         
14      return total + value * value;                               
15   }                                                              
16
17   // Class template SumSquaresClass defines overloaded operator()
18   // that adds the square of its second argument and running     
19   // total in its first argument, then returns sum               
20   template<typename T>
21   class SumSquaresClass {
22   public:
23      // add square of value to total and return result            
24      T operator()(const T& total, const T& value) {               
25         return total + value * value;                             
26      }                                                            
27   };                                                           
28
29   int main() {
30      const size_t SIZE{10};
31      array<int, SIZE> integers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
32      ostream_iterator<int> output{cout, " "};
33
34      cout << "array integers contains: ";
35      copy(integers.cbegin(), integers.cend(), output);
36
37      // calculate sum of squares of elements of array integers
38      // using binary function sumSquares
39      int result{accumulate(integers.cbegin(), integers.cend(),     
40         0, sumSquares)};                                           
41
42      cout << "

Sum of squares of integers' elements using "
43         << "binary function sumSquares: " << result;
44
45      // calculate sum of squares of elements of array integers
46      // using binary function object
47      result = accumulate(integers.cbegin(), integers.cend(),       
48         0, SumSquaresClass<int>{});                                
49
50      cout << "

Sum of squares of integers' elements using binary"
51         << "
function object of type SumSquaresClass<int>: "
52         << result;
53
54      // calculate sum of squares array's elements using a lambda
55      result = accumulate(integers.cbegin(), integers.cend(),          
56         0, [](auto total, auto value){return total + value * value;});
57
58      cout << "

Sum of squares of integer's elements using a lambda: "
59         << result << endl;
60   }

array integers contains: 1 2 3 4 5 6 7 8 9 10

Sum of squares of integers' elements using binary function sumSquares: 385

Sum of squares of integers' elements using
binary function object of type SumSquaresClass<int>: 385

Sum of squares of integer's elements using a lambda: 385

Function sumSquares

Lines 13–15 define a function sumSquares that squares its second argument value, adds that square and its first argument total and returns the sum. Function accumulate will pass each of the elements of the sequence over which it iterates as the second argument to sumSquares in the example. On the first call to sumSquares, the first argument will be the initial value of the total (which is supplied as the third argument to accumulate; 0 in this program). All subsequent calls to sumSquares receive as the first argument the running sum returned by the previous call to sumSquares. When accumulate completes, it returns the sum of the squares of all the elements in the sequence.

Class SumSquaresClass

Lines 20–27 define the class template SumSquaresClass with an overloaded operator() that has two parameters and returns a value—the requirements for a binary function object. On the first call to the function object, the first argument will be the initial value of the total (which is supplied as the third argument to accumulate; 0 in this program) and the second argument will be the first element in array integers. All subsequent calls to operator() receive as the first argument the result returned by the previous call to the function object, and the second argument will be the next element in the array. When accumulate completes, it returns the sum of the squares of all the elements in the array.

Calling Algorithm accumulate

Lines 39–40 call function accumulate with a pointer to function sumSquares as its last argument. Similarly, the statement in lines 47–48 call accumulate with an object of class SumSquaresClass as the last argument. Finally, lines 55–56 call accumulate with an equivalent lambda.

The expression SumSquaresClass<int>{} in line 48 creates (and calls the default constructor for) an object of class SumSquaresClass (a function object) that’s passed to accumulate, which invokes the function operator(). Lines 47–48 could be written as two separate statements, as follows:


SumSquaresClass<int> sumSquaresObject;
result = accumulate(integers.cbegin(), integers.cend(),
   0, sumSquaresObject);

The first line defines an object of class SumSquaresClass, which is then passed to accumulate in the subsequent statement.

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

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