If you are not using STL algorithms yet, you should be. In this article, I'll show how to enjoy using STL algorithms without being repelled by the seemingly unavoidable awkwardness of the standard member function adapters.
With all due respect towards STL algorithms, I am certainly sympathetic with those clinging to archaic, sub-optimal, and unsafe constructs such as:
for (vector::iterator it = v.begin();
it != v.end(); ++it)
{
it->do_something();
}
After all, from time to time, I myself am guilty of using such relics despite
all the persuasive arguments from the masters [1]. Okay, okay, I am still working
on it, although I'll step forward and say that true friends do not ask others
to remember and maintain torturous constructs such as:
class Foo { ... void draw() const; };
extern void draw(const Foo&);
for_each(b, e, ptr_fun(draw));
for_each(b, e, mem_fun(&Foo::draw));
for_each(b, e, mem_fun_ref(&Foo::draw));
The first line passes a non-member function (draw) to an algorithm. The
second line passes a member function (&Foo::draw) to an algorithm working
on the sequence of pointers to objects. The third line passes a member function
(&Foo::draw) to an algorithm working on the sequence of objects.
Confusing? It certainly is to me. If you found yourself grumbling about "if-this-if-that" scenarios and ptr_fun, mem_fun, and mem_fun_ref "beauties," you are not alone.
As Scott Meyers puts it, "They are unpleasant to type, annoying to read, and resistant to comprehension" (see [1], Item 41). I would most certainly prefer using STL algorithms in a friendlier way. For instance, without ptr_fun, mem_fun, and mem_fun_ref:
class Widget
{ ...
void draw() const
bool move();
};
extern void draw(const Widget&);
vector<Widget> v1;
vector<Widget*> v2;
for_each(b1, e1, draw);
for_each(b1, e1, &Widget::draw);
for_each(b1, e1, &Widget::move);
for_each(b2, e2, &Widget::draw);
It turns out that getting rid of adapters is disappointingly easy to achieve.
Listing 1 shows the extended aux::for_each
interface consisting of three overloaded functions. The first function (lines
3-9) is the original std::for_each interface, which is still used with
non-member functions (e.g., draw(const Widget&)) and function adapters
(e.g., bind2nd). The other two functions (lines 11-33) are called respectively
for const and non-const member functions (e.g., Widget::draw).
They merely call the standard for_each algorithm with an appropriate functor
object (lines 20 and 32). In the std namespace, you have to call mem_fun
"convenience" functions to create those functors themselves. The following
code shows the guts of the std::mem_fun declared in <functional>.
template <class R, class T>
mem_fun_t<R, T>
mem_fun(R (T::*func)())
{
return mem_fun_t<R, T>(func);
}
template <class R, class T>
const_mem_fun_t<R, T>
mem_fun(R (T::*func)() const)
{
return const_mem_fun_t<R, T>(func);
}
As aux::for_each functions create appropriate functors directly, in the
aux namespace, you do not need mem_fun function adapters. You might
have already guessed it -- you do not need mem_fun_ref either. Those
adapters are the same as mem_fun, but they return functors of the mem_fun_ref_t
and const_mem_fun_ref_t types. (If you stumbled on these types and your
eyes refused to read them properly, they have _ref_t at the end.) The std::mem_fun_t
and std::mem_fun_ref_t look very much alike:
// Simplified and beautified versions
// of the standard functors.
template <class Return, class Type>
class mem_fun_t
{
typedef Return (Type::*Func)();
public:
explicit mem_fun_t(Func func)
: func_(func) {}
Return operator()(Type* obj) const
{
return (obj->*func_)();
}
private: Func func_;
};
template <class Return, class Type>
class mem_fun_ref_t
{
typedef Return (Type::*Func)();
public:
explicit mem_fun_t(Func func)
: func_(func) {}
Return operator()(Type& obj) const
{
return (obj.*func_)();
}
private: Func func_;
};
The only differences are that the operator functions accept different types
(pointers and references) and, consequently, use pointer or reference syntax to
call a member function. Therefore, it appears only logical to merge the functionality
into one aux::mem_fun_t class:
namespace aux {
template <class Return, class Type>
class mem_fun_t
{
typedef Return (Type::*Func)();
public:
explicit mem_fun_t(Func func)
: func_(func) {}
Return operator()(Type* obj) const
{
return (obj->*func_)();
}
Return operator()(Type& obj) const
{
return (obj.*func_)();
}
private: Func func_;
};
}
Or if you prefer closer relations with the std::mem_fun_t cousin:
template <class R, class T>
struct mem_fun_t : std::mem_fun_t<R, T>
{
typedef R (T::*Func)();
typedef std::mem_fun_t<R, T> super;
explicit mem_fun_t(Func func)
: super(func) {}
R operator()(T* obj) const
{
return super::operator()(obj);
}
R operator()(T& obj) const
{
return super::operator()(&obj);
}
};
The aux::const_mem_fun_t is constructed in the same fashion. The complete
source code is available at <www.cuj.com/code/>.