Discussion
Team LiB
Previous Section Next Section

Discussion

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.

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



Instead, you need:



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.

    Team LiB
    Previous Section Next Section