DiscussionFirst, 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. You don't have to remember this stuff if you're using a correctly-written function object (see 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. |