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));
}