It gets pretty tedious for me to write code like
class date {
private:
int day, year, month;
int comp(const date & Other) const;
public:
date(int D, int Y, int M) : day (D), year (Y), month (M) {}
friend bool operator==(const date & L, const date & R) { return L.comp(R) == 0 ; }
friend bool operator!=(const date & L, const date & R) { return L.comp(R) != 0 ; }
friend bool operator> (const date & L, const date & R) { return L.comp(R) > 0 ; }
friend bool operator>=(const date & L, const date & R) { return L.comp(R) >= 0 ; }
friend bool operator< (const date & L, const date & R) { return L.comp(R) < 0 ; }
friend bool operator<=(const date & L, const date & R) { return L.comp(R) <= 0 ; }
}
All that relational operators stuff is boilerplate (same for every single class that needs them). There must surely be a better way to do all this. I know that C++ 20 offers the spaceship operator, but I don't have that. So my question is this: How do I avoid this boilerplate code using older than C++ 20 features?
You could use Boost.Operators (or implement such a thing yourself), but you'd still have to write two functions:
class date : public totally_ordered<date> {
private:
int day, year, month;
int comp(const date & Other) ;
public:
date(int D, int Y, int M) : day (D), year (Y), month (M) {}
friend bool operator==(const date & L, const date & R) { return L.comp(R) == 0 ; }
friend bool operator< (const date & L, const date & R) { return L.comp(R) < 0 ; }
}
Note that you generally don't want to use the pattern of defining ==
in terms of a three-way ordering since ==
can be much more efficient in general (e.g. consider the case of strings where you know differently-sized strings are unequal right away, but you don't know what the result of compare()
would be without actually comparing an arbitrarily large number of characters).
Note also that a general problem with this approach is deciding whether x <= y
is defined as !(y < x)
or x < y || x == y
. Most library solutions choose the former (fewer comparisons), though this gives the wrong answer on partial orders - which was part of the motivation for <=>
to begin with. Boost.Operators addresses this by also having a partially_ordered<T>
which correctly defines <=
in terms of both operations.
There must surely be a better way to do all this.
As Nicol says, that's why we added <=>
:
class date {
private:
int year, month, day; // NB: I reordered these
public:
friend auto operator<=>(const date&, const date&) = default;
};