The predefined functors in Table 16.12 are all
What makes a functor adaptable is that it carries typedef members identifying its argument types and return type. The members are called result_type, first_argument_type, and second_argument_type, and they represent what they sound like. For example, the return type of a plus
The significance of a functor being adaptable is that it can then be used by function adapter objects, which assume the existence of these typedef members. For example, a function with an argument that is an adaptable functor can use the result_type member to declare a variable that matches the function’s return type.
Indeed, the STL provides function adapter classes that use these facilities. For example, suppose you want to multiply each element of the vector gr8 by 2.5. That calls for using the transform() version with a unary function argument, like the example shown earlier:
transform(gr8.begin(), gr8.end(), out, sqrt);
The multiplies() functor can do the multiplication, but it’s a binary function. So you need a function adapter that converts a functor that has two arguments to one that has one argument. The earlier TooBig2 example shows one way, but the STL has automated the process with the binder1st and binder2nd classes, which convert adaptable binary functions to adaptable unary functions.
Let’s look at binder1st. Suppose you have an adaptable binary function object f2(). You can create a binder1st object that binds a particular value, called val, to be used as the first argument to f2():
binder1st(f2, val) f1;
Then, invoking f1(x) with its single argument returns the same value as invoking f2() with val as its first argument and f1()’s argument as its second argument. That is, f1(x) is equivalent to f2(val, x), except that it is a unary function instead of a binary function. The f2() function has been adapted. Again, this is possible only if f2() is an adaptable function.
This might seem a bit awkward. However, the STL provides the bind1st() function to simplify using the binder1st class. You give it the function name and value used to construct a binder1st object, and it returns an object of that type. For example, you can convert the binary function multiplies() to a unary function that multiplies its argument by 2.5. Just do this:
bind1st(multiplies
Thus, the solution to multiplying every element in gr8 by 2.5 and displaying the results is this:
transform(gr8.begin(), gr8.end(), out,
bind1st(multiplies
The binder2nd class is similar, except that it assigns the constant to the second argument instead of to the first. It has a helper function called bind2nd that works analogously to bind1st.
Listing 16.16 incorporates some of the recent examples into a short program.
Listing 16.16. funadap.cpp
// funadap.cpp -- using function adapters
#include
#include
#include
#include
#include
void Show(double);
const int LIM = 6;
int main()
{
using namespace std;
double arr1[LIM] = {28, 29, 30, 35, 38, 59};
double arr2[LIM] = {63, 65, 69, 75, 80, 99};
vector
vector
cout.setf(ios_base::fixed);
cout.precision(1);
cout << "gr8:\t";
for_each(gr8.begin(), gr8.end(), Show);
cout << endl;
cout << "m8: \t";
for_each(m8.begin(), m8.end(), Show);
cout << endl;
vector
transform(gr8.begin(), gr8.end(), m8.begin(), sum.begin(),
plus
cout << "sum:\t";
for_each(sum.begin(), sum.end(), Show);
cout << endl;
vector
transform(gr8.begin(), gr8.end(), prod.begin(),
bind1st(multiplies
cout << "prod:\t";
for_each(prod.begin(), prod.end(), Show);
cout << endl;
return 0;
}
void Show(double v)
{
std::cout.width(6);
std::cout << v << ' ';
}
Here is the output of the program in Listing 16.16:
gr8: 28.0 29.0 30.0 35.0 38.0 59.0
m8: 63.0 65.0 69.0 75.0 80.0 99.0
sum: 91.0 94.0 99.0 110.0 118.0 158.0
prod: 70.0 72.5 75.0 87.5 95.0 147.5
C++11 provides an alternative to function pointers and functors. It’s called a
Algorithms
The STL contains many nonmember functions for working with containers. You’ve seen a few of them already: sort(), copy(), find(), for_each(), random_shuffle(), set_union(), set_intersection(), set_difference(), and transform(). You’ve probably noticed that they feature the same overall design, using iterators to identify data ranges to be processed and to identify where results are to go. Some also take a function object argument to be used as part of the data processing.