C++ operator overloading, understanding the Google style guide

I am following a book to learn C++ (come from a python background). I've written this, which works:

class CatalogueItem
{
    public:
        CatalogueItem();
        CatalogueItem(int item_code, const string &name, const string &description);
        ~CatalogueItem() {};

        bool operator< (const CatalogueItem &other) const;
        ...

    private:
        ...
};

...

list<CatalogueItem> my_list;

// this is just me playing around
CatalogueItem items[2];
items[0] = CatalogueItem(4, string("box"), string("it's a box"));
items[1] = CatalogueItem(3, string("cat"), string("it's a cat"));

my_list.push_back(items[0]);
my_list.push_back(items[1]);

my_list.sort();

The part I'm trying out is using the operator < to allow the list to sort itsself.

This all seems good, but http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Operator_Overloading seems to suggest avoiding doing this, which is exactly what the book says to do! ("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 understand "create equality and comparison functor types" to mean creating comparison functions, like the below one:

bool my_comparison_function(const CatalogueItem &a, const CatalogueItem &b)
{
    // my comparison code here
}

Is that what the style guide is referring to?

Does anyone have an option as to which method is more "correct"?

J

13.10.2009 15:57:13
I don't think the Google style guide is really the best set of C++ guidelines out there. If you read it, they explicitly state in several sections that decisions have been made for their own internal reasons rather than because they are "right".
alex tingle 13.10.2009 16:44:08
Google essentially has a "no fun" approach to C++, which perhaps is necessary in an organisation containing thousands of talented programmers who have to somehow be persuaded to produce mutually intelligible C++. But their style guide makes me honestly wonder why they made the decision to use C++ at all, rather than C together with some convention to mock up an equivalent to virtual calls. It just seems cruel to the programmers to dangle all kinds of possibilities in front of them, and then snatch them away ;-)
Steve Jessop 13.10.2009 17:55:02
It is quite normal to overload the operator < and operator =. The problem here is that people in the early days abused operator overloading and the side affects are often not easy to see when reading the code. So be sensiable.
Martin York 13.10.2009 18:50:16
"people in the early days abused operator overloading" - not least the standards committee, with auto_ptr.
Steve Jessop 14.10.2009 11:29:32
@SteveJessop: Has somebody done an in-depth break down of the "Google Style Guide" that I can reference. Something that shows what is good/bad/ugly in their recommendations for C++.
Martin York 24.02.2012 22:25:55
3 ОТВЕТА
РЕШЕНИЕ

A functor type would be more like this:

struct CatalogueItemLessThan
{
    bool operator()(const CatalogueItem &a, const CatalogueItem &b)
    {
    }    
};

Then the usage would look like this:

list<CatalogueItem> my_list;

// this is just me playing around
CatalogueItem items[2];
items[0] = CatalogueItem(4, string("box"), string("it's a box"));
items[1] = CatalogueItem(3, string("cat"), string("it's a cat"));

my_list.push_back(items[0]);
my_list.push_back(items[1]);

my_list.sort(CatalogueItemLessThan());

The main advantage of this, is that is allows you to decouple sorting from the object itself. You can now provide as many types of sorting as you want, and use them in different places. (For example, string can be sorted in lexical order, or case-insensitively, or "naturally".

The advantage of using a functor as opposed to a loose function is that you can pass parameters into the comparison to modify how the functor should behave.

In general, the Google style-guide is not really the best style guide out there (IMHO especially their taking exception to exceptions, but that's another discussion). If an object has an obvious sorting order, I often add in a default operator<. If later, there are extra sort orders I want to add, then I add in loose functions. If at a later time, I need to add parameters to the sort order, then I make them into functors. There's no sense in adding in complexity before it's needed.

10
23.05.2017 12:19:37
Yep, this is what they mean; such classes are sometimes known as comparators.
David Seiler 13.10.2009 16:09:12
The other main advantage of using a functor is that you can use a functor as a type parameter to templates such as std::map, which you cannot do with a plain function.
Pavel Minaev 13.10.2009 16:11:32
Good, but absolutely incorrect! :) You can use ordinary functions as parameters to template classes. Ordinary functions (decaying to function pionters) are Functors as well, and they work perfectly well as comparators with 'std::map' (for example). Just try it. The main benefit of a class-based functor is that you can attach an internal state to that functor, i.e. add data fields to the class. Ordinary functions can't have such a state attached to them. Otherwise, there's no difference between class-based functors and ordinary functions.
AnT 13.10.2009 17:02:00
@Greg Rogers: That's true (ignoring what some super-advanced compiler can do). Also, class-based functors sometimes are more resistant to ambiguities in overload resolution. However, this doesn't change the fact that ordinary functions are Functors as well and work fine wherever Functors are allowed.
AnT 13.10.2009 17:30:20
That's not how you use it. You have to pass a function pointer type as the type argument to the template, of course. The function pointer itself is passed as an argument to the contructor. For example: std::map<int, int, bool (*)(int, int)> map(testLess)
AnT 13.10.2009 18:20:29

A functor type is a C++ type (class or struct), that overloads the () operator so that instances of the type behave like a function. This is similar to a class implementing __call__() in Python.

Some STL collection types like std::map require a key_compare functor to order the keys in interal tree structures and thus providing fast access times. By default, this is std::less, which uses operator< to compare values. Therefore this operator is often provided to allow custom classes to act as keys in std::map (and similar).

Google obviously discourages this in favor of supplying your own comparison functor. So, instead of implementing operator<, you could do the following:

struct my_compare
{
    bool operator ()(const CatalogueItem& lhs, const CatalogueItem& rhs)
    {
        ...
    }
};

If you must access private members to implement this, declare the functor as a friend of your class.

1
13.10.2009 16:10:59
Functor is a more generic concept than what you describe above. Functor is everything that accepts the postfix '()' operator. Ordinary function or function pointer is also a functor. It doesn't have to be an object of class type with overloaded '()'.
AnT 13.10.2009 16:13:31
However, just to nit-pick, I think Ferdinand is right in defining the term "functor type". He didn't say "a functor is". +1 for adding the friend recommendation!
Andy Dent 14.10.2009 03:07:45

What Google is trying to say to you is the following.

As you know, you can overload one and only one operator '<' for a given type. Let's say it works for you. But imagine that in the future you might need to sort objects of the same type in accordance with some other comparison criterion. How are you going to do that? The only available version of '<' is already taken.

Of course, you can do that by writing a new named comparison function/functor (not the '<' operator) and explicitly supplying it to the sorting algorithm. You can write 2, 5, 10 more of them. You can write as many as you want. It will work. However, at that point there will be an obvious asymmetry in your code. One comparison function is implemented as 'operator <'. The others - as different named functions/functors. Is there a good reason for this asymmetry?

Well, there might be. If you have a very well-defined and obvious natural sorting method that applies to your type, it makes a very good sense to implement it as operator '<'. This would be the main comparison method. And other, auxiliary, less "natural" comparison methods can and should be implemented as named functions. This is prefectly fine.

However, what if you don't have such an obvious candidate for the "natural" comparison? In this case favoring one method over the other and "wasting" the '<' operator on an arbitrarily chosen one is not a good idea. In this case it is recommended to leave the '<' alone, and stick to named functions/functors instead.

In other words, by overloading the '<' you create a "favorite" comparison for the given type. If that's what you really want - go ahead and do it. But keep in mind that in many cases creating an artificial and arbitrary "favorite" is not a good idea. Don't rush the process of choosing that favorite. Don't take the '<' too early.

9
14.10.2009 02:54:13
Good points on why this is a recommendation. On the other hand, many types only ever need to be sorted one way. It's not too difficult to switch from using operator< to a functor if you own all the code, the main difficulty is if you are producing a library for others to use and can't just bounce changes off the compiler.
tfinniga 13.10.2009 16:17:46