Let's consider class like that with less than operator:
struct Test
{
int value;
constexpr bool operator<(const Test& p_rhs) const
{
return value < p_rhs.value;
}
};
I would like to generate bool operator>(const Test&, const Test&),bool operator<=(const Test&, const Test&) and bool operator>=(const Test&, const Test&) without using CRTP(or any other inheritance) on Test class on generic way, that could be also used on other classes that have bool operator<(const T&,const T&). I tried sth like that:
template<typename Lhs, typename Rhs = Lhs>
struct GenerateOtherComparisonsFromLessThan
{
constexpr friend bool operator>(const Rhs& p_rhs, const Lhs& p_lhs)
{
return p_lhs < p_rhs;
}
//and rest of other comparison operators
};
struct InstantiateTestComparisons : GenerateOtherComparisonsFromLessThan<Test>
{};
But it looks like these overloads are not taken(at least on gcc 8.3 on ideone).
In C++, for the operator greater than or equal to (">="), is it enough to have the operators equal ("=") and greater (">") overloaded to have functionality for the greater than or equal to (">=")? Or do I need to overload the operator (">=") to have functionality for it?
operator >= is not a combination of operator > and operator =. operator >= is its own operator but you can implement it in terms of operator < Typically you would have something like
inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator!=(const X& lhs, const X& rhs){return !operator==(lhs,rhs);}
inline bool operator< (const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator> (const X& lhs, const X& rhs){return operator< (rhs,lhs);}
inline bool operator<=(const X& lhs, const X& rhs){return !operator> (lhs,rhs);}
inline bool operator>=(const X& lhs, const X& rhs){return !operator< (lhs,rhs);}
From sbi's answer on What are the basic rules and idioms for operator overloading?
is it enough to have the operators equal ("=")
Equal operator in c++ is ==
OR do I need to overload the operator (">=") to have functionality for it?
It depends what you mean by functionality. If you mean that if you define operator== and operator> will compiler generate operator>= automagically for you? No, it would not, you have to implement it using existing operators or not.
No, C++ does not write those operators for you.
If you think that sucks, you are right. A bunch of ways to make this suck less have been done. I'll talk about 4 of them.
Wait for c++20
In c++20, if you write operator<=> (the 3-way "spaceship" operator) properly, or =default it, then all of <, <=, >=, >, != and == will be written for you.
struct bob {
int x,y;
auto operator<=>( bob const& )const = default;
};
The above bob has every < == etc operator written for it by C++ now.
Just write them
Prior to c++20 you have to write all of them if you want all of them. This is tedious and error-prone.
Using std::tie and invoking < and the like on them is slightly less error-prone:
struct bob {
int x, y;
friend bool operator<( bob const& lhs, bob const& rhs ) {
return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y);
}
};
or even
struct bob {
int x, y;
friend auto as_tie( bob const& b ) { // C++14
return std::tie(b.x, b.y);
}
friend bool operator<( bob const& lhs, bob const& rhs ) {
return as_tie(lhs) < as_tie(rhs);
}
};
because tuple does a proper lexographic comparison; writing lexographic comparions without bugs is annoying.
Metaprogram your way around it
When comparing strings you usually use strcmp. This returns a negative number if less, a positive number if greater, and 0 if equal. This pattern can be more efficient than doing < or == repeatedly.
Making a single strcmp like function produce < == and the other comparison operations can be done:
namespace utils {
template<class D>
struct use_cmp {
friend bool operator<( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) < 0;
}
friend bool operator>( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) > 0;
}
friend bool operator<=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) <= 0;
}
friend bool operator>=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) >= 0;
}
friend bool operator==( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) == 0;
}
friend bool operator!=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) != 0;
}
private:
D const& self() const { return *static_cast<D const*>(this); }
};
}
Now supose we have a type:
struct bob {
int x, y;
};
and we want to be able to use comparison operators on it:
struct bob : utils::use_cmp<bob>
{
int x, y;
bob( int x_, int y_ ):x(x_), y(y_) {} // constructor
friend int cmp( bob const& lhs, bob const& rhs ) {
if (lhs.x < rhs.x) return -1;
if (lhs.x > rhs.x) return 1;
if (lhs.y < rhs.y) return -1;
if (lhs.y > rhs.y) return 1;
return 0;
}
};
and using the magic of CRTP bob now has every comparison operator written for it.
Live example.
That annoying friend int cmp (which gets more annoying the more members you have in it) can be handled by yet more boilerplate helper code:
namespace utils {
template<class...Ts>
int cmp( std::tuple<Ts...> const& lhs, std::tuple<Ts...> const& rhs );
template<class T, class...LowPriority>
int cmp( T const& lhs, T const& rhs, LowPriority&&... );
template<class...Ts, std::size_t...Is>
int tuple_cmp( std::tuple<Ts...> const& lhs, std::tuple<Ts...> const& rhs, std::index_sequence<Is...> ) {
int result = 0;
( (result = cmp( std::get<Is>(lhs), std::get<Is>(rhs) )) && ... );
return result;
}
template<class...Ts>
int cmp( std::tuple<Ts...> const& lhs, std::tuple<Ts...> const& rhs ) {
return tuple_cmp( lhs, rhs, std::make_index_sequence<sizeof...(Ts)>{} );
}
template<class T, class...LowPriority>
int cmp( T const& lhs, T const& rhs, LowPriority&&... ) {
if (lhs < rhs) return -1;
if (rhs < lhs) return 1;
return 0;
}
}
which is yet more arcane code, but you get a simpler bob:
struct bob : utils::use_cmp<bob>
{
int x, y;
bob( int x_, int y_ ):x(x_), y(y_) {}
friend auto as_tie(bob const& b) {
return std::tie(b.x,b.y);
}
friend int cmp( bob const& lhs, bob const& rhs ) {
return utils::cmp( as_tie(lhs), as_tie(rhs) );
}
};
Note, however, that all of this is done and better by operator<=> in c++20.
Live example.
Use someone else's solution
This is similar to the approach boost::operators uses to write these operators for you.
Using an obvious notation, "> || ==" is actually an over-requirement for >=.
Although note that for all the relational operators, you only actually need <, since equivalence is established if a < b and b < a are both false. In fact this is one of the concepts used in ordered C++ standard library containers.
It is a frequent task of mine to write all the overloaded comparison operators to a class, so I've written a template class which implements <,<=,>=,!= if the derived class implements == and <. It is working but features a lot of cast and the not that obvious "Curiously recurring template pattern", so I wonder if are there simpler solutions?
template <class Derived>
class Comparable
{
public:
bool operator!=(const Comparable<Derived>& other) {
return !(static_cast<Derived*>(this)->operator==
(*static_cast<const Derived*>(&other)));
}
bool operator<=(const Comparable<Derived>& other) {
return (static_cast<Derived*>(this)->operator==
(*static_cast<const Derived*>(&other)))
|| (static_cast<Derived*>(this)->operator<
(*static_cast<const Derived*>(&other)));
}
bool operator>(const Comparable<Derived>& other) {
return !(static_cast<Derived*>(this)->operator==
(*static_cast<const Derived*>(&other)))
&& !(static_cast<Derived*>(this)->operator<
(*static_cast<const Derived*>(&other)));
}
bool operator>=(const Comparable<Derived>& other) {
return !(static_cast<Derived*>(this)->operator<
(*static_cast<const Derived*>(&other)));
}
};
In case it is not obvious from the description in the comment:
template <typename T>
struct Comparable {
friend bool operator!=(T const & lhs, T const & rhs) { return !(lhs == rhs); }
friend bool operator> (T const & lhs, T const & rhs) { return rhs < lhs; }
// ...
};
class MyType : Comparable<MyType> {
int data;
friend bool operator==(MyType const & lhs, MyType const & rhs) {
return lhs.data == rhs.data;
}
friend bool operator< (MyType const & lhs, MyType const & rhs) {
return lhs.data < rhs.data;
}
public:
// ...
};
When the compiler encounters MyType a, b; a > b; lookup for the operator will end up doing ADL which will look inside MyType and Comparable<MyType> (as this is a base), where it will find the implementation you need: bool operator>(MyType const&, MyType const&).
The operators being free functions allows for a definition that is outside of the type that is being compared (in this case the base), while making those operators only available through ADL (one of the two arguments must be Comparable<MyType>). The use of a free function also provides type-symmetry, the compiler will allow implicit conversions on both sides, where in the case of a member function it would only allow conversions on the right hand side of the operator.
For completeness, a different trick that can be done is to provide the operators as templates in a namespace together with a tag that can be used to bring that namespace in for ADL purposes:
namespace operators {
template <typename T>
bool operator>(T const & lhs, T const & rhs) {
return rhs < lhs;
}
// rest of the operators come here
struct tag {};
}
class MyType : operators::tag {
int data;
friend bool operator<(T const & lhs, T const & rhs) {
return lhs.data < rhs.data;
}
//...
};
The trick is basically the same, except that in this case the operators are not found inside the base, but in a namespace that is associated with it. This solution is a bit less nice than the previous one, as it is open to different forms of misuse, including using namespace operators; that would make the templated operators available for all types.
I've implemented the functionality of std::rel_ops namespace as a template base class (it defines all comparison operators using only operators < and ==). For me it's a bit weird that it works (so far) properly, also I'm concerned about the 'hacks' used. Can anyone assess the following code and say if I'm just lucky it to work or it's standard practice to do things like that.
template <typename T>
class RelationalOps {
public:
inline bool operator!=(const T &rhs) const
{
const T& lhs = static_cast<const T&>(*this);
return !(lhs == rhs);
}
inline bool operator<=(const T &rhs) const
{
const T& lhs = static_cast<const T&>(*this);
return ((lhs < rhs) || (lhs == rhs));
}
inline bool operator>(const T &rhs) const
{
const T& lhs = static_cast<const T&>(*this);
return !((lhs < rhs) || (lhs == rhs));
}
inline bool operator>=(const T &rhs) const
{
const T& lhs = static_cast<const T&>(*this);
return !(lhs < rhs);
}
};
Well, why not use Boost.Operators?
I'm in the process of designing several classes that need to support operators !=, >, <=, and >=. These operators will be implemented in terms of operators == and <.
At this stage, I need to make a choice between inheritance¹ and forcing my consumers to use std::rel_ops² "manually".
[1] Inheritance (possible implementation):
template<class T> class RelationalOperatorsImpl
{
protected:
RelationalOperatorsImpl() {}
~RelationalOperatorsImpl() {}
friend bool operator!=(const T& lhs, const T& rhs) {return !(lhs == rhs);}
friend bool operator>(const T& lhs, const T& rhs) {return (rhs < lhs);}
friend bool operator<=(const T& lhs, const T& rhs) {return !(rhs < lhs);}
friend bool operator>=(const T& lhs, const T& rhs) {return !(lhs < rhs);}
};
template<typename T> class Foo : RelationalOperatorsImpl< Foo<T> >
{
public:
explicit Foo(const T& value) : m_Value(value) {}
friend bool operator==(const Foo& lhs, const Foo& rhs) {return (lhs.m_Value == rhs.m_Value);}
friend bool operator<(const Foo& lhs, const Foo& rhs) {return (lhs.m_Value < rhs.m_Value);}
private:
T m_Value;
};
[2] std::rel_ops glue:
template<typename T> class Foo
{
public:
explicit Foo(const T& value) : m_Value(value) {}
friend bool operator==(const Foo& lhs, const Foo& rhs) {return (lhs.m_Value == rhs.m_Value);}
friend bool operator<(const Foo& lhs, const Foo& rhs) {return (lhs.m_Value < rhs.m_Value);}
private:
T m_Value;
};
void Consumer()
{
using namespace std::rel_ops;
//Operators !=, >, >=, and <= will be instantiated for Foo<T> (in this case) on demand.
}
I'm basically trying to avoid code repetition. Any thoughts as to which method "feels" better?
Have you considered using boost, and having your class inherit from boost::less_than_comparable<T> and boost::equality_comparable<T>? It is akin to your first suggestion, with some pros and cons. Pros: avoids code duplication; Cons: creates a dependency on boost.
Since boost is a very common C++ library (if you don't use it already, you should seriously consider start using it), the con factor is dimmed.
I think std::rel_ops is quite nice, but there's one thing to consider first: std::rel_ops provides operators as template functions that accept two parameters of the same type. Because most conversions (including e.g. arithmetic promotions and user-defined conversions) are not performed when template argument deduction occurs, this means that you would not be able to use any of these additional operators (e.g. !=) with such conversions.
E.g. if you have a class MyInt that attempts to behave like a regular integer, you might have written conversion functions/constructors or templated operators so that you can do
MyInt x, y;
x < 5;
9 == x;
However,
x > 5;
30 <= x;
won't work (with std::rel_ops) because the two arguments are of different types, so template argument deduction will fail.