First, function objects are easy to make adaptable, and always should be (see Item 89). Even if you already have a function, sometimes you have to wrap it in
ptr_fun or
mem_fun anyway to add adaptability. For example, you have to do this in order to build up more complex expressions using binders (see also Item 84):
inline bool IsHeavy( const Thing& ) {
/*…*/ } find_if( v.begin(), v.end(),
not1( IsHeavy ) );
// error: isn't adaptable
The workaround is to insert
ptr_fun (or, for a member function,
mem_fun or
mem_fun_ref ):
inline bool IsHeavy( const Thing& ) {
/*…*/ } find_if( v.begin(), v.end(),
not1( ptr_fun<Thing,void>( IsHeavy ) ) );
// ok: now it's adaptable
Aside: Yes, it's a pain that here you need to explicitly specify
ptr_fun 's template arguments. This is another drawback to using functions. Briefly, the reason the template arguments are needed is that
ptr_fun deduces the argument and return types exactly and creates a
pointer_to_unary_function , which in turn helpfully tries to add another
& , and references to references are not currently allowed by ISO C++. There are ways in which
ptr_fun could, and probably should, be fixed so as to strip top-level
const and
& from non-pointer parameter and return types (see Item 89), but it doesn't do that today.Item 89), which is adaptable from the get-go without special syntax:
struct IsHeavy : unary_function<Thing, bool> { bool operator()( const Thing& ) const {
/*…*/ } }; find_if( v.begin(), v.end(),
not1( IsHeavy() ) );
// ok: adaptable
More importantly, you need a function object, not a function, to specify comparers for associative containers. This is because it's illegal to instantiate a template type parameter with a function type directly:
bool CompareThings( const Thing&, const Thing& ); set<Thing,
CompareThings > s;
// error
struct CompareThings : public binary_function<Thing,Thing,bool> { bool operator()( const Thing&, const Thing& ) const; }; set<Thing,
CompareThings > s;
// ok
Finally, there is also an efficiency benefit. Consider this familiar algorithm:
template<typename Iter, typename Compare> Iter find_if( Iter first, Iter last,
Compare comp );
If we pass a function as the comparer to
find_if
inline bool
Function ( const Thing& ) {
/*…*/ } find_if( v.begin(), v.end(),
Function );
we're actually passing a reference to
Function . Compilers rarely inline such function calls (except as part of whole-program analysis, which is still a relatively recent feature on popular compilers), even when as above the function is declared
inline and is visible while compiling the
find_if call. And, as noted, functions aren't adaptable.
If we pass a function object as the comparer to
find_if
struct FunctionObject : unary_function<Thing, bool> { bool
operator() ( const Thing& ) const {
/*…*/ } }; find_if( v.begin(), v.end(),
FunctionObject() );
we're passing an object that typically has an (implicitly or explicitly) inline
operator() function. Compilers have routinely inlined such calls since C++'s Bronze Age.
Note: This is not to encourage premature optimization (see Item 8), but to discourage premature pessimization (see Item 9). If you already have a function, go ahead and pass a pointer to the function (unless you have to wrap it with
ptr_fun or
mem_fun anyway). But if you're writing a new piece of code for use as an argument to an algorithm, prefer writing the extra boilerplate to make it a function object.