vesii
vesii

Reputation: 3138

Do we need to overload operator< in order to use set.find?

Let's say I have a class called Person and another class called School. In the second class I have a private field which is a set (of the standard library). The set represents the students (objects of the first class). In one of the functions of School, I want to check if a studet does not exist in the set of students. For that I do:

if (students.find(student) == students.end()) {
    // throw exception
}

I was wondering if I need to overload one of the comparison operators of Person because otherwise how find knows to compare the objects? Reading the docs I see that this method is only using operator<. Does it mean that I have to overload it in Person? If I don't overload it, does it use a default one? Does the compiler creates a default operator less? Otherwise, as I understand, it should fail building?

Upvotes: 1

Views: 400

Answers (2)

Chris Uzdavinis
Chris Uzdavinis

Reputation: 6131

Overloading the "less than" operator for a type is an option, but not necessarily the only way.

When you create a std::set, you can provide a comparator to compare objects. By default, the comparator it uses is std::less, which will use operator<, but your comparator can do anything else, provided it meets the requirements of ordering properly.

Some examples:

#include <set>
#include <functional>

struct EvenOddIncreasing {
    bool operator()(int a, int b) const noexcept { 
        if ((a&1) < (b&1)) return true;
        if ((a&1) > (b&1)) return false;
        return a < b;
    }
};

std::set<int, EvenOddIncreasing> evenOddIncreasing{1,2,3,4,5};
std::set<int, std::greater<>> decreasing{1,2,3,4,5};
std::set<int, std::less<>> increasing{1,2,3,4,5};

The code above uses an int, which has operator< obviously defined for it, but since the std::set is passed explicit comparator types, it will use those for ordering. Note, evenOddIncreasing is an example of a custom comparator that puts evens first, then odds, and in increasing order.

The above sets will have the following orderings:

even odd increasing...2 4 1 3 5
decreasing...5 4 3 2 1
increasing...1 2 3 4 5

In C++20, you could even use a lambda, but not with older versions of C++.

See it live:

https://godbolt.org/z/9ExhEGbb6

Upvotes: 1

Botond Horv&#225;th
Botond Horv&#225;th

Reputation: 1017

1.: Actually overloading operator< is just 1 way the other way is to define a functor and give it to the set eg.:

class Comper_functor{
    bool operator()(const Person& a, const Person& b);
}
...
std::set<Person,Comper_functor> s;

works just fine if you want to avoid overloading operator<

2.: operator< (or the compere function) does not have to be a "logical" operator just a consistent one for std::set. So it is good if for any Person a,b,c

a) if a<b and b<c -> a<c

b) if a<b -> !b>a

c) if !a<b and !b<a -> a==b

The easiest way to make it work is compering the 2 person member by member.

Eg.: for a

class Ponit{
    double x,y;
    public:
    bool operator<(const point& o)const{
        if (x!=o.x) return x<o.x;
        return y<o.y;
    }
}

works fine. Even if a point is can't be "smaller" then an other one.

3.: in c++20 you can say in the class definition

std::weak_ordering operator<=>(const Person& o)const=default;
bool operator==(const Person& o)const=default;

To generate all operators <,>,<=,>=,==,!=

In older versions you have to define operator< manually

Upvotes: 4

Related Questions