简体   繁体   中英

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

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM