Function objects


Objects with operator()

 - simulating (global) functions
 - efficiency: better inline-ing
 - avoiding typical errors


Naive approach:


template<class T> class Sum {
    T res;
public:
    Sum(T i = 0) : res(i) { }           // initialize
    void operator()(T x) { res += x; }  // accumulate
    T result() const { return res; }    // return sum
};

//Usage of a function objects
void f(list<double>& ld)
{
    Sum<double> s;
    s = for_each(ld.begin(),ld.end(),s);    // invoke s() for each element of ld
    cout << "the sum is " << s.result() << '\n';
}



More structured approach:


template <class Arg, class Res> struct unary_function {
    typedef Arg argument_type;
    typedef Res result_type;
};

template <class Arg, class Arg2, class Res> struct binary_function {
    typedef Arg first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Res result_type;
};

template <class T> struct logical_not : public unary_function<T,bool> {
    bool operator()(const T& x) const { return !x; }
};

template <class T> struct less : public binary_function<T,T,bool> {
    bool operator()(const T& x, const T& y) const { return x<y; }
};



Binders


template <class T> class less_than : public unary_function<T,bool> {
    T arg2;
public:
    explicit less_than(const T& x) : arg2(x) { }
    bool operator()(const T& x) const { return x<arg2; }
};


// Now, we can write the following:
void f(list<int>& c)
{
    list<int>::const_iterator p = find_if(c.begin(),c.end(),less_than<int>(7));
    // ...
}


// In a more general way is implemented in STL:
template <class BinOp>
class binder2nd : public unary_function<BinOp::first_argument_type, BinOp::result_type> {
protected:
    BinOp op;
    typename BinOp::second_argument_type arg2;
public:
    binder2nd(const BinOp& x, const typename BinOp::second_argument_type& v)
        : op(x), arg2(v) { }
    result_type operator()(const argument_type& x) const { return op(x,arg2); }
};

template <class BinOp, class T> binder2nd<BinOp> bind2nd(const BinOp& op, const T& v)
{
    return binder2nd<BinOp>(op,v);
}


// Usage of a binder
void f(list<int>& c)
{
    list<int>::const_iterator p = find_if(c.begin(),c.end(),bind2nd(less<int>(),7));
    // ...
}


template <class T> struct less_than : public binder2nd< less<T> > {
    explicit less_than(const T& x) : binder2nd(less<T>(),x) { }
};

void f(list<int>& c)
{
    list<int>::const_iterator p = find_if(c.begin(),c.end(),less_than<int>(7));
    // ...
}



Similarly, there are negators...



Member function adapter


// This is syntax error!
void draw_all(list<Shape*>& c)
{
    for_each(c.begin(),c.end(),&Shape::draw);   // oops! error
}


/*
 *  In the STL:
 *
 */
template<class R, class T> class mem_fun_t : public unary_function<T*,R> {
    R (T::*pmf)();
public:
    explicit mem_fun_t(R (T::*p)()) :pmf(p) {}
    R operator()(T* p) const { return (p->*pmf)(); }    // call through pointer
};

template<class R, class T> mem_fun_t<R,T> mem_fun(R (T::*f)())
{
    return mem_fun_t<R,T>(f);
}


/*
 *  Usage of a member function adapter
 *
 */
void draw_all(list<Shape*>& lsp) // call 0-argument member through pointer to object
{
    for_each(lsp.begin(),lsp.end(),mem_fun(&Shape::draw));  // draw all shapes
}


/*
 *  And the same with arguments
 *
 */
template<class R, class T> mem_fun_t<R,T> mem_fun(R (T::*f)());
// and versions for unary member, for const member, and const unary member (see table in _algo.adapter_)

template<class R, class T> mem_fun_ref_t<R,T> mem_fun_ref(R (T::*f)());
// and versions for unary member, for const member, and const unary member (see table in _algo.adapter_)


/*
 *  Usage
 *
 */
void f(list<string>& ls)
// use member function that takes no argument for object
{
    typedef list<string>::iterator LSI;
    LSI p = find_if(ls.begin(),ls.end(),mem_fun_ref(&string::empty));   // find ""
}

void rotate_all(list<Shape*>& ls, int angle)
// use member function that takes one argument through pointer to object
{
    for_each(ls.begin(),ls.end(),bind2nd(mem_fun(&Shape::rotate),angle));
}