views:

1301

answers:

7

In the Google C++ Style Guide, the section on Operator Overloading recommends against overloading any operators ("except in rare, special circumstances"). Specifically, it recommends:

In particular, do not overload operator== or operator< just so that your class can be used as a key in an STL container; instead, you should create equality and comparison functor types when declaring the container.

I'm a little fuzzy on what such a functor would look like, but my main question is, why would you want to write your own functors for this? Wouldn't defining operator<, and using the standard std::less<T> function, be simpler? Is there any advantage to using one over the other?

+3  A: 

Well, according to the webpage you cite, there's not much advantage for functors ("[operators] can fool our intuition into thinking that expensive operations are cheap, built-in operations.")

My felling is that one should strive to make you classes first-class objects as much as possible, which, to me, means having them understand as many operators as sensible.

It's been a while since I written a functor, but it would look something like this:

class MyClass {....}

class LessThanMyClass : std:binary_function<MyClass, MyClass, bool>
{
    public bool operator()(MyClass lhs, MyClass rhs) 
    {   return /* determine if lhs < rhs */ ; }
}

vector<MyClass> objs;
std::sort(objs.begin(), objs.end(), LessThanMyClass());

}

James Curran
+1  A: 

A functor is a class with an operator () method. In this case the method would take two parameters of the type being compared and return a bool result if the first is less than the second.

Edit: to build on what James Curran said, you can define your functor inside the class. So, for example:

class MyClass
{
    struct LessThan : public std::binary_function<MyClass, MyClass, bool>
    {
        bool operator()(const MyClass & first, const MyClass & second) const
        {
            return first.key < second.key;
        }
    };
};
Mark Ransom
+1  A: 

Ironically, a functor also requires overriding an operator (the () operator), so I'm not sure what their point is.

Jim Buck
+12  A: 

Except for the more fundamental types, the less-than operation isn't always trivial, and even equality may vary from situation to situation.

Imagine the situation of an airline that wants to assign all passengers a boarding number. This number reflects the boarding order (of course). Now, what determines who comes before who? You might just take the order in which the customers registered – in that case, the less-than operation would compare the check-in times. You might also consider the price customers paid for their tickets – less-than would now compare ticket prices.

… and so on. All in all, it's just not meaningful to define an operator < on the Passenger class although it may be required to have passengers in a sorted container. I think that's what Google warns against.

Konrad Rudolph
+2  A: 

Generally, defining operator< is better and simpler.

The case where you would want to use functors is when you need multiple ways of comparing a particular type. For example:

class Person;

struct CompareByHeight {
    bool operator()(const Person &a, const Person &b);
};

struct CompareByWeight {
    bool operator()(const Person &a, const Person &b);
};

In this case there may not be a good "default" way to compare and order people so not defining operator< and using functors may be better. You could also say that generally people are ordered by height, and so operator< just calls CompareByHeight(), and anyone who needs Person's to be ordered by weight has to use CompareByWeight explicitly.

Often times the problem is that defining the functors is left up to the user of the class, so that you tend to get many redefinitions of the same thing, whenever the class needs to be used in an ordered container.

Greg Rogers
+1  A: 

I probably wouldn't go as far as the Google style guide.

I think that what they are getting at is that when you overload operator< and operator==, you are making a decision for every use of the type, while the functor types only apply to that collection.

If the only thing you need the comparitors for is to put the item in a collection, then it's better to have a function specifically for that context rather than the operators that would apply in all contexts.

You may want to sort purchase orders chronologically, but in general, it would make sense to compare them by their total price. If we overload operator< to compare dates so that we can load them into a collection, we are introducing the risk that another client may misuse our operator< which they may think compares the total prices.

JohnMcG
+1  A: 

I think the message behind not defining operator< is that ordering is a property of the collection, not of the object. Different collections of the same objects may have different orderings. So you should use a separate functor used when specifying the type of the collection rather than operator<.

In practice though, a lot of your classes may have a natural ordering and that is the only ordering used in collections in your application. In other cases, the ordering may not even be relevant to the application, just the collection so it can find items later. In these cases, it makes perfect sense to define operator<.

Remember, when we're desiging object models, we're only modelling a subset of the real world. In the real world there may be umpteen different ways to rank objects of the same class, but in the application domain in which we are working there may be one that is relevant.

If code evolves to need a second ordering that is just as relevant as the first, the class should be refactored to remove operator< and to place both ranking functions in separate functors. This shows the intent that no one ranking is more important than the others.

With regard to arithmetic operators, you should not overload these unless you are implementing an arithmetic type.

Of course, there are exceptions to every rule. If you don't know whether or not you should be making an exception, you probably should not. Experience will be your guide.

camh