Function pointer adapters
Wherever a function-like entity is expected by an algorithm, you can supply either a pointer to an ordinary function or a function object. When the algorithm issues a call, if it is through a function pointer, than the native function-call mechanism is used. If it through a function object, then that objects operator( ) member executes. You saw earlier, for example, that we passed a raw function, gt15( ), as a predicate to remove_copy_if( ) in the program CopyInts2.cpp. We also passed pointers to functions returning random numbers to generate( ) and generate_n( ).
You cannot, however, use raw functions with function object adapters, such as bind2nd( ), because they assume the existence of type definitions for the argument and result types. Instead of manually converting your native functions into function objects yourself, the standard library provides a family of adapters to do the work for you. The ptr_fun( ) adapters take a pointer to a function and turn it into a function object. They are not designed for a function that takes no arguments—they must only be used with unary functions or binary functions.
The following program uses ptr_fun( ) to wrap a unary function.
//: C06:PtrFun1.cpp
// Using ptr_fun() with a unary function
#include
#include
#include
#include
#include
#include
using namespace std;
int d[] = {123, 94, 10, 314, 315};
const int dsz = sizeof d / sizeof *d;
bool isEven(int x) {
return x % 2 == 0;
}
int main() {
vector
transform(d, d + dsz, back_inserter(vb),
not1(ptr_fun(isEven)));
copy(vb.begin(), vb.end(),
ostream_iterator
cout << endl;
// Output: 1 0 0 0 1
} ///:~
We can’t simply pass isEven to not1, because not1 needs to know the actual argument type and return type its argument uses. The ptr_fun( ) adapter deduces those types through template argument deduction. The definition of the unary version of ptr_fun( ) looks something like this:.
template
pointer_to_unary_function
ptr_fun(Result (*fptr)(Arg))
{
return pointer_to_unary_function
}
As you can see, this version of ptr_fun( ) deduces the argument and result types from fptr and uses them to initialize a pointer_to_unary_function object that stores fptr. The function call operator for pointer_to_unary_function just calls fptr, as you can see by the last line of its code:.
template
class pointer_to_unary_function
: public unary_function
Result (*fptr)(Arg); // stores the f-ptr
public:
pointer_to_unary_function(Result (*x)(Arg))
: fptr(x) {}
Result operator()(Arg x) const {return fptr(x);}
};
Since pointer_to_unary_function derives from unary_function, the appropriate type definitions come along for the ride and are available to not1.
There is also a binary version of ptr_fun( ), which returns a pointer_to_binary_function object (which derives from binary_function, of course) that behaves analogously to the unary case. The following program uses the binary version of ptr_fun( ) to raise numbers in a sequence to a power. It also reveals a "gotcha" when passing overloaded functions to ptr_fun( ).
//: C06:PtrFun2.cpp
// Using ptr_fun() for a binary function
#include
#include
#include
#include
#include
#include
using namespace std;
double d[] = { 01.23, 91.370, 56.661,
023.230, 19.959, 1.0, 3.14159 };
const int dsz = sizeof d / sizeof *d;
int main() {
vector
transform(d, d + dsz, back_inserter(vd),
bind2nd(ptr_fun
copy(vd.begin(), vd.end(),
ostream_iterator
cout << endl;
} ///:~
The pow( ) function is overloaded in the standard C++ header
float pow(float, int); // efficient int power versions…
double pow(double, int);
long double pow(long double, int);
float pow(float, float);
double pow(double, double);
long double pow(long double, long double);
Since there are multiple versions of pow( ), the compiler has no way of knowing which to choose. In this case, we have to help the compiler by using explicit function template specialization, as explained in the previous chapter.
An even trickier problem is that of converting a member function into a function object suitable for using with the generic algorithms. As a simple example, suppose we have the classical "shape" problem and want to apply the draw( ) member function to each pointer in a container of Shape:.
//: C06:MemFun1.cpp
// Applying pointers to member functions
#include
#include
#include
#include
#include "../purge.h"
using namespace std;
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {}
};
class Circle : public Shape {
public:
virtual void draw() {
cout << "Circle::Draw()" << endl;
}
~Circle() {
cout << "Circle::~Circle()" << endl;
}
};