Выбрать главу

The following program uses ptr_fun( ) to wrap a unary function.

//: C06:PtrFun1.cpp

// Using ptr_fun() with a unary function

#include <algorithm>

#include <cmath>

#include <functional>

#include <iostream>

#include <iterator>

#include <vector>

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<bool> vb;

  transform(d, d + dsz, back_inserter(vb),

    not1(ptr_fun(isEven)));

  copy(vb.begin(), vb.end(),

    ostream_iterator<bool>(cout, " "));

  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 <class Arg, class Result>

pointer_to_unary_function<Arg, Result>

ptr_fun(Result (*fptr)(Arg))

{

  return pointer_to_unary_function<Arg, Result>(fptr);

}

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 Arg, class Result>

class pointer_to_unary_function

: public unary_function<Arg, Result> {

  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 <algorithm>

#include <cmath>

#include <functional>

#include <iostream>

#include <iterator>

#include <vector>

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<double> vd;

  transform(d, d + dsz, back_inserter(vd),

    bind2nd(ptr_fun<double, double, double>(pow), 2.0));

  copy(vd.begin(), vd.end(),

    ostream_iterator<double>(cout, " "));

  cout << endl;

} ///:~

The pow( ) function is overloaded in the standard C++ header <cmath> for each of the floating-point data types, as follows:

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 <algorithm>

#include <functional>

#include <iostream>

#include <vector>

#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;

  }

};

class Square : public Shape {

public:

  virtual void draw() {

    cout << "Square::Draw()" << endl;

  }

  ~Square() {

    cout << "Square::~Square()" << endl;

  }

};

int main() {

  vector<Shape*> vs;

  vs.push_back(new Circle);

  vs.push_back(new Square);

  for_each(vs.begin(), vs.end(),

    mem_fun(&Shape::draw));

  purge(vs);

} ///:~

The for_each( ) algorithm does just what it sounds like it: it passes each element in a sequence to the function object denoted by its third argument. In this case, we want the function object to wrap one of the member functions of the class itself, and so the function object’s "argument" becomes the pointer to the object that the member function is called for. To produce such a function object, the mem_fun( ) template takes a pointer to a member as its argument. The purge( ) function is just a little something we wrote that calls delete on every element of sequence.

The mem_fun( ) functions are for producing function objects that are called using a pointer to the object that the member function is called for, while mem_fun_ref( ) is used for calling the member function directly for an object. One set of overloads of both mem_fun( ) and mem_fun_ref( ) is for member functions that take zero arguments and one argument, and this is multiplied by two to handle const vs. non-const member functions. However, templates and overloading takes care of sorting all that out; all you need to remember is when to use mem_fun( ) vs. mem_fun_ref( ).