views:

255

answers:

6

Hi All,
I have two questions related to function objects and function pointers,


Question : 1

When I read the different uses sort algorithm of STL, I see that the third parameter can be a function objects, below is an example

class State {  
  public:  
    //...
    int population() const;  
    float aveTempF() const;  
    //...  
};    
struct PopLess : public std::binary_function<State,State,bool> {  
    bool operator ()( const State &a, const State &b ) const  
        { return popLess( a, b ); }  
};  
sort( union, union+50, PopLess() );  

Question :

Now, How does the statement, sort(union, union+50,PopLess()) work? PopLess() must be resolved into something like PopLess tempObject.operator() which would be same as executing the operator () function on a temporary object. I see this as, passing the return value of overloaded operation i.e bool (as in my example) to sort algorithm.

So then, How does sort function resolve the third parameter in this case?


Question : 2

Question

Do we derive any particular advantage of using function objects versus function pointer? If we use below function pointer will it derive any disavantage?

inline bool popLess( const State &a, const State &b )
    { return a.population() < b.population(); }  
std::sort( union, union+50, popLess ); // sort by population

PS : Both the above references(including example) are from book "C++ Common Knowledge: Essential Intermediate Programming" by "Stephen C. Dewhurst".
I was unable to decode the topic content, thus have posted for help.

Thanks in advance for your help.

+6  A: 

PopLess() instantiates a temporary instance of the class PopLess to be passed to std::sort(). It's effectively the same as if you were to say (note that in this example an extra copy is made):

PopLess pl = PopLess();
sort(union, union + 60, pl);

Then, std::sort() will call the operator() on that instance.

As for whether function objects or function pointers are better "better," it depends. Probably the most important difference is that function objects can maintain state while ordinary functions passed by pointer cannot. A compiler might be able to optimize one or the other better, but in most usage scenarios that's probably not important.

James McNellis
Can't ordinary functions passed by pointer maintain state in multiple ways, such as: static local variables, static module variables (at "global" scope within the C compilation unit but not visible from without), and possibly other means?
Heath Hunnicutt
I think, what James is referring is - at function pointer level - there is no scope of state maintainance for different invocation. So if you are using function pointer and are calling sort many times, you cannot have different state for each. This can be achieved by function objects. Correct me if I am wrong.
kumar_m_kiran
@James: Usually the function object causes faster code than the function pointer, because compilers often cannot inline calls made through function pointers, while the `operator()()` member function of the function object can easily be inlined.
sbi
@Heath: Using global variables is what got us into the mess prompting someone to invent an OO version of good old C. `:)`
sbi
A: 
  • Question 1: PopLess() is a temporary object.
  • Question 2: it allows your "function" to have state... you can initialize the object with whatever you like before it gets used as a function
Keith Nicholas
+2  A: 

I'm not sure what Question 1 is asking, but PopLess() is an object. Inside the sort function, this object has its operator() method called to compare the items.

Michael
+1  A: 

Q1: PopLess() constructs an object of type PopLess and sort then uses this object to sort the elements in the range using the operator ().

Looking at the for_each function might be easier, it could be implemented like this:

template <typename IterT, typename Function>
Function for_each( IterT first, IterT last, Function f ) {
    for( ; first != last; ++first )
        f(*first);

    return f;
}

So basically for_each and sort, and functions using function objects, simply take an instance of your function object and call its operator ().

Q2: A compiler might be able to optimize away the function call by inlining when you are using a function object rather than a function pointer, this might not be as straight forward for the compiler to do with the function pointer. Also, a function object can have state.

Jacob
I wouldn't say that a call through a function pointer can't be optimized out. For example, if the algorithm is expanded inline where it is called, and if the function pointer used at that location is always the same, then it would be possible to inline the call through the function pointer as well. It might be more difficult to inline an indirect function call, but I don't write compilers, so I don't really know.
James McNellis
I tried to adjust my answer to reflect your comment, "can't" is a strong term to use when you don't know-
Jacob
+1  A: 

Question 1:

PopLess() must be resolved into something like PopLess > tempObject.operator() which would be same as executing the operator () function on a temporary object.

It's not [expanded as you said]. PopLess() is actually the call to the implicit PopLess::PopLess() constructor. This code creates a temporary object and passes it to the 3rd parameter in the function call.

Question : 2

Do we derive any particular advantage of using function objects versus function pointer?

Not in this case. Here your PopLess object is stateless. You can create functors (function objects) that have internal state.

Example:

struct ensure_min
{
    int value;
    ensure_min(int val) : value(val) {}
    int operator()(const int& i)
    {
        return std::max(value, i);
    }
}

std::vector<int>  values;
values.push_back(-1);
values.push_back(0);
values.push_back(1);
values.push_back(2);
values.push_back(3);

std::transform(values.begin(), values.end(), 
    std::ostream_iterator<int>(std::cout, "\n"), ensure_min(1));

This code will output all the numbers in the sequence, ensuring all the numbers in the output have a minimal value of 1 (the output is either 1 - if the original number was any less - or the original number if the original number was greater than or equal to 1).

utnapistim
Thanks for a detailed explanation!!!
kumar_m_kiran
A: 

The main practical difference is the ability for a functor to maintain state. For example, sorting multi-column data the functor can have info on which column to sort by, the sort direction and even collation rules (case sensitivity etc.).

Michael J