2

I found this code on Wikipedia

class compare_class {
  public:
  bool operator()(int A, int B) const {
    return A < B;
  }
};
...
// Declaration of C++ sorting function.
template <class ComparisonFunctor> 
void sort_ints(int* begin_items, int num_items, ComparisonFunctor c);
...
int main() {
    int items[] = {4, 3, 1, 2};
    compare_class functor;
    sort_ints(items, sizeof(items)/sizeof(items[0]), functor);
}

At first I wondered how the A and B parameters got passed to operator()(int A, int B), when the functor was mentioned in sort_ints without even any parenthesis.

Then I figured that A and B are being passed to the function object inside the sort_ints function. But then, shouldn't the declaration of sort_ints have 'ComparisonFunctor*** c' instead of 'ComparisonFunctor c', since it's receiving the address of a function?

Inside the sort_ints function, would the function call to the functor be done something like this?

functor(*begin_items, *(begin_items+1));

4 Answers 4

6

To understand why sort_ints takes its parameter by value, you have to remember that the object being passed into it as the comparator is not necessarily a function pointer. If, for example, you're using the compare_class function object to do the comparison, then what you're passing into the function is a concrete object of type compare_class. You're not passing in the address of compare_class::operator(), since operator() is a member function, not a free function. That is, the following code:

compare_class myComparator;
myComparator(a, b);

translates into

compare_class myComparator;
myComparator.operator() (a, b);

Consequently, the parameter needs to take in the comparator by value rather than by pointer, since it needs a receiver object, rather than a pointer to a member function or a pointer to the receiver object.

10
  • Does that mean that the third parameter of sort_ints is actually taking an entire object by value? Won't that be bad/inefficient if we're working with large objects?
    – Nav
    Commented Jan 31, 2011 at 5:55
  • 1
    @Nav: yes - a reference would be better. But, a comparison object rarely has much state, and is only passed once regardless of the number of items being sorted, so it's not likely to be a big deal. Commented Jan 31, 2011 at 6:01
  • @Nav- The compiler is usually really good at aggressively optimizing the receiver object out of the picture when invoking operator(). Coupled with the fact that the compiler can resolve which function is being called at compile-time (since it knows the type of the receiver), it's often much faster to use function objects than raw functions! (Though it does make the executable bigger) Commented Jan 31, 2011 at 6:13
  • Thanks. I figured the best way to have the parameters of sort_ints would have been void sort_ints(int* begin_items, int num_items, const ComparisonFunctor& c);
    – Nav
    Commented Jan 31, 2011 at 6:35
  • @Nav: if you use the function object as state machine, your approach won't work.
    – Donotalo
    Commented Jan 31, 2011 at 7:39
3

As you've noted, there's nothing in the usage of sort_ints that hints at its requirements of the comparison functor:

compare_class functor;
sort_ints(items, sizeof(items)/sizeof(items[0]), functor);

There's nothing in sort_ints itself either:

template <class ComparisonFunctor>
void sort_ints(int* begin_items, int num_items, ComparisonFunctor c);

And in class_compare, we can only deduce the functionality that will be used by observing it offers no other functionality:

class compare_class
{
  public:
     bool operator()(int A, int B) const { return A < B; }
};

The compiler does indeed leave it until it tries to compile the implementation of sort_ints, using the types with which it's instantiated, before it works out whether this all hangs together. sort_ints will have to have exactly the kind of statement you mention:

c(*begin_items, *(begin_items+1));    // note: functor argument is "c"

So, your understanding is correct on all counts. BUT, it's worth noting that the proposed C++0x feature called Concepts was intended to make the requirements of sort_int more explicit without looking at its implementation. Unfortunately, C++0x had to abandon this feature as there's been insufficient time and experience to ensure it's optimal. Hopefully a future version of C++ will incorporate such a feature. You can find lots of discussions of Concepts on the 'net, and they should help you understand the overall issue better.

It's an important issue because when you've only pieces of the puzzle - such as a sort_ints function you want to use but no example ComparisonFunctor, then you have to study the implementation of sort_ints to know how to create that Functor (unless you're super lucky and there's good, current documentation). You may accidentally make your code too dependent on the existing implementation, so that your code breaks or slows down unacceptably when the implementation of sort_int is changed slightly later - even if it is subtly enough not to break the test cases or other users' code or expectations. It's also likely that a small mistake in the provided ComparisonFunctor will produce a very convoluted and confusing compiler error message from somewhere in the guts of sort_int - that's not a nice way to provide a user with an abstracted service. Let's hope Concepts makes it in one day...!

2

Notice that, compare_class is a class. So it can be declared, passed as function parameter the way you use other classes.

Secondly, compare_class implementes operator(). This allows you to do the following:

compare_class obj;
obj(1, 2);

Now, the second statement in the above code fragment seems like a function call! So in a sense, any object of a class that implements operator() can be use like a function.

That's the point of function objects. Besides, it has other advantages over function pointers which you will find in the same link you've given in your post.

EDIT

Inside sort_ints(), functor is expected to do something like the following:

for (int i = 1; i < num_items; i++)
    if (c(begin_items[i-1], begin_items[i]))
    {
        // begin_items[i-1] is less than begin_items[i], do stuff
    }
1
  • Thanks, but I'm not trying to understand the general concept of function pointers. I'm trying to understand how the values get passed to it in this particular example since the inside code of sort_ints isn't available and because sort_ints does not seem to be receiving a function pointer. I'm puzzled as to how it works.
    – Nav
    Commented Jan 31, 2011 at 5:44
1

Yes, that's correct. The function call operator is applied to the name of the object, not a method of the object. Furthermore, you are not passing an address of a function at all, you are passing (copying) an object. functor objects have no data members, only an operator ( ).

Not the answer you're looking for? Browse other questions tagged or ask your own question.