What is the preferred method of using std::rel_ops to add the full set of relational operators to a class?
This documentation suggests a using nam
The problem with adding the rel_ops namespace, regardless of whether you do it with a manual using namespace rel_ops; or whether you do it automatically as described in the answer by @bames53 is that adding the namespace can have unanticipated side effects on portions of your code. I found this myself just recently as I had been using the @bames53 solution for some time, but when I changed one of my container based operations to use a reverse_iterator instead of an iterator (within a multimap but I suspect it would be the same for any of the standard containers), suddenly I was getting compile errors when using != to compare two iterators. Ultimately I tracked it down to the fact that the code included the rel_ops namespace which was interfering with how reverse_iterators are defined.
Using boost would be a way to solve it, but as mentioned by @Tom not everyone is willing to use boost, myself included. So I implemented my own class to solve the problem, which I suspect is also how boost does it, but I didn't check the boost libraries to see.
Specifically, I have the following structure defined:
template
struct add_rel_ops {
inline bool operator!=(const T& t) const noexcept {
const T* self = static_cast(this);
return !(*self == t);
}
inline bool operator<=(const T& t) const noexcept {
const T* self = static_cast(this);
return (*self < t || *self == t);
}
inline bool operator>(const T& t) const noexcept {
const T* self = static_cast(this);
return (!(*self == t) && !(*self < t));
}
inline bool operator>=(const T& t) const noexcept {
const T* self = static_cast(this);
return !(*self < t);
}
};
To use this, when you define your class, say MyClass, you can inherit from this one to add the "missing" operators. Of course you need to define the == and < operators within MyClass (not shown below).
class MyClass : public add_rel_ops {
...stuff...
};
It is important that you include MyClass as the template argument. If you were to include a different class, say MyOtherClass, the static_cast would be almost certain to give you problems.
Note that my solution is assuming that the == and < operators are defined as const noexcept which is one of the requirements of my personal coding standards. If your standards are different, you would need to modify add_rel_ops accordingly.
In addition, if you are bothered by the use of static_cast, you can change them to be a dynamic_cast by adding
virtual ~add_rel_ops() noexcept = default;
to the add_rel_ops class in order to make it a virtual class. Of course, that will also force MyClass to be a virtual class which is why I do not take that approach.