I have a class with an attribute of type std::string. I'd like to provide some comparison operator functions like <, >, ==, <=, and >= for the class by comparing the attribute.
My questions is that: any easy way or tool to
(1) just write one or two functions, such as the one for operator < (and ==), others can be automatically generated.
(2) or even simpler since the class comparison is depending on its attribute of type std::string
whose comparison functions are already provided.
Curiously recurring template pattern
In this case you provide a simple base class which implements all needed operators and simply inherit from it:
template <class T>
struct implement_relational_operators{
friend bool operator<=(const T & a,const T & b){ return a < b || a == b; }
friend bool operator>(const T & a, const T & b){ return !(a <= b); }
friend bool operator!=(const T & a, const T & b){ return !(a == b);}
friend bool operator>=(const T & a, const T & b){ return !(a < b); }
};
template <class T>
struct scalar : public implement_relational_operators<scalar<T> >{
T value;
bool operator<(const scalar& o) const { return value < o.value;}
bool operator==(const scalar& o) const { return value == o.value;}
};
This doesn't share the drawbacks from std::rel_ops
(see below). However, you still need to implement operator<
and operator==
. A similar trick is used by boost
.
C++20's <=>
might improve the situation further (see below).
std::rel_ops
(deprecated in C++20)
std::rel_ops
provides the additional operations based on <
an ==
, so you just need to write two operators.
However, it will be deprecated in C++20, where a <=> b
will get thrown into the mix.
Requirements
You just need the equality (operator=
) and the lesser than (operator<
) comparison operator. The rest can be generated automatically with std::rel_ops
, since the following holds:
a != b equal to !(a == b)
a <= b equal to (a < b) || (a == b)
a >= b equal to !(a < b)
a > b equal to !(a <= b)
Note that these are less efficient than writing those by hand.
Warning
However, since it's easy to provide those operators yourself you should just take the extra effort and write them. And they also have some drawbacks, as mentioned by R. Martinho Fernandes:
Note that [they] won't work properly unless you:
- add
using namespace std::rel_ops
on every context you use the operators; or
- add
using::operator!=; using::operator<=; using::operator>=; using::operator>;
in the namespace of your class (using namespace std::rel_ops
in the namespace of your class is not acceptable because it does not get picked up by ADL).
You could use std::rel_ops
, but it would be wrong. That's a sledgehammer that doesn't belong in anyone's toolkit.
The usual approach is to define operator==
and operator<
with knowledge of the details of the class. The remaining four operators can be written with those:
a != b
is !(a == b)
a <= b
is !(b < a)
a > b
is b < a
a >= b
is !(a < b)
One option is to use CRTP such as in Barton Nackman trick:
https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
https://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick
// A class template to express an equality comparison interface.
template<typename T> class equal_comparable {
friend bool operator==(T const &a, T const &b) { return a.equal_to(b); }
friend bool operator!=(T const &a, T const &b) { return !a.equal_to(b); }
};
class value_type
// Class value_type wants to have == and !=, so it derives from
// equal_comparable with itself as argument (which is the CRTP).
: private equal_comparable<value_type> {
public:
bool equal_to(value_type const& rhs) const; // to be defined
};
(If you have control over the class) the correct way to do this is by using Boost.Operators http://www.boost.org/doc/libs/1_66_0/libs/utility/operators.htm
#include<cassert>
#include<boost/operators.hpp>
struct A : boost::totally_ordered<A> // implies equality-comparable and less-than-comparable
{
int val_;
A(int const& v) : val_{v}{}
bool operator==(A const& other) const{return other.val_ == val_;}
bool operator<(A const& other) const{return other.val_ < val_;}
};
int main(){
A a1{5};
A a2{7};
assert(!(a1 == a2)); // user defined operator==
assert(a1 != a2); // automatically defined !=
assert(a1 < a2); // user defined operator<
assert(a2 > a1); // automatically defined >
assert(a2 >= a1); // automatically defined >=
assert(a1 <= a2); // automatically defined <=
}
Boost.Operators uses CRTP (see other answers), but does everything for you.
I just learnt from the other answers about std::rel_ops
, however my premature conclusion is that it is not very useful.
Boost.Operators seems to be more powerful and customizable, although it is a bit intrusive.
Note that you have to know which of the operations to implement (in this case operator==
and operator<
. But in principle you could just define operator!=
and operator>
.
It would be cool for the other operators to be automatically "deduced", see Very automatic operator generator in C++
This is another example, where only one operator is enough. (But probably inefficient?)
#include<boost/operators.hpp>
struct A : boost::totally_ordered<A>, boost::equivalent<A>{
int val_;
A(int const& v) : val_{v}{}
bool operator<(A const& other) const{return other.val_ < val_;}
};