Relational Operator Implementation Dilemma - c++

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.

Related

Generating comparison operators without CRTP

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).

Operator overloader not making an affect

I am attempting to create a class that takes a priority queue and reverses the priorities. I include a bool operator<() function as a member function of the class, but no matter how I structure this function, the operator never seems to be overloaded.
Here is my class:
template<typename T>
class MinPQ{
public:
bool empty() const {
return pq.empty();
}
unsigned int size() const {
return pq.size();
}
void push(const T& element){
pq.push(element);
}
const T& min() const {
return pq.top();
}
void remove_min(){
pq.pop();
}
bool operator<(const T& element) const {
return pq < element;
}
private:
priority_queue<T> pq;
};
EDIT
I also tried changing the overloading function to the following and do not undertsand why that isn't working either.
bool operator<(const T& element){
return this < element;
}
Your operator< is meaningless. The only thing it can do is comparing MinPQ<T> object with T object. If you want to compare two T objects you should do this:
1) If the template type T can take only a handful of types, you can write comparison operators explicitly for each of these types:
bool operator<(const T& lhs, const T& rhs)
{
return lhs > rhs;
}
note: each operator< should be a non-member function. Or a member function of class T with a single argument.
2) write a comparison functor:
template <class T> struct CompareT
{
bool operator()(const T& lhs, const T& rhs) const
{
return lhs > rhs;
}
};
and then declare pq member as follows:
priority_queue<T, std::vector<T>, CompareT<T>> mq;
3) If you want simply to inverse priorities, you could simply use std::greater class:
priority_queue<T, std::vector<T>, std::greater<T>> mq;

Template class implementing comparison operators

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.

C++ relational operators generator

Once you define the < operator, you can have an estimation of how the rest of relational operators behave. I'm trying to implement a way to do that for my classes.
What I want is to define only the < and the rest of the operators to be defaulted implicitly. What I've got so far is this design, which I'll elaborate on further down:
template<typename T>
struct relational
{
friend bool operator> (T const &lhs, T const &rhs) { return rhs < lhs; }
friend bool operator==(T const &lhs, T const &rhs) { return !(lhs < rhs || lhs > rhs); }
friend bool operator!=(T const &lhs, T const &rhs) { return !(rhs == lhs); }
friend bool operator<=(T const &lhs, T const &rhs) { return !(rhs < lhs); }
friend bool operator>=(T const &lhs, T const &rhs) { return !(lhs < rhs); }
};
So for a class that implements the < operator it would just take inheriting from relational to have the rest of the operators defaulted.
struct foo : relational<foo>
{
// implement < operator here
};
Are there any alternatives, better designs ?
Is there a time bomb in this code? I'm assuming that if a user wants to define a custom implementation for one of the operators, the overload resolution would kick and select the non template (user defined) implementation. If that's not the case (or I would have problem with class templates inheriting from relational) should I implement the operators in relational like this ?
// inside the relational struct
friend bool operator>(relational const &lhs, relational const &rhs)
{ // functions that involve implicit conversion are less favourable in overload resolution
return (T const&)rhs < (T const&)lhs;
}
Thanks for your advices, here's a demo of the code working
I usually use a trick I learned from Robert Martin to do this.
I have a template class:
template <typename T>
class ComparisonOperators
{
protected:
~ComparisonOperators() {}
public:
friend bool operator==( T const& lhs, T const& rhs )
{
return lhs.compare( rhs ) == 0;
}
friend bool operator!=( T const& lhs, T const& rhs )
{
return lhs.compare( rhs ) != 0;
}
friend bool operator<( T const& lhs, T const& rhs )
{
return lhs.compare( rhs ) < 0;
}
friend bool operator<=( T const& lhs, T const& rhs )
{
return lhs.compare( rhs ) <= 0;
}
friend bool operator>( T const& lhs, T const& rhs )
{
return lhs.compare( rhs ) > 0;
}
friend bool operator>=( T const& lhs, T const& rhs )
{
return lhs.compare( rhs ) >= 0;
}
};
The class which needs the operators derives from this:
class Toto : public ComparisonOperators<Toto>
{
// ...
public:
// returns value < 0, == 0 or >0, according to
// whether this is <, == or > other.
int compare( Toto const& other ) const;
};
(My implementation is actually a bit more complicated, since it
uses some simple meta-programming to call isEqual, rather than
compare, if that function exists.)
EDIT:
And rereading your question: this is basically what you're doing, and it's pretty much the standard idiom for this sort of thing. I prefer using named functions like compare, but that is just a personal preference. The meta-programming trick to handle isEqual, however, is worth the bother: it means that you can use the same class for types which only support equality; you'll get an error when the compiler tries to instantiate e.g. operator<=, but the compiler won't try to instantiate it unless someone uses it. And it's often the case the isEqual can be implemented a lot more efficiently than compare.
EDIT 2:
For what it's worth: I do this systematically. I also have
ArithmeticOperators (defining e.g. + in terms of +=),
MixedTypeArithmeticOperators (like the above, but with two
types, T1, for which it is a base class, and T2; it
provides all of the combination of operators). and
STLIteratorOperators, which implements the STL iterator
interface based on something more rational and easier to
implement (basically, the GoF iterator with an isEqual
function). They saves a lot of boilerplate.
EDIT 3:
And finally: I just looked at the actual code in my toolkit.
Conditionally supporting isEqual is even simpler than
I remembered: the template class above has a public member:
bool isEqual( T const& other ) const
{
return static_cast< T const* >( this )->compare( other ) == 0;
}
And operator== and operator!= just use isEqual, no
template meta-programming involved. If the derived class
defines an isEqual, it hides this one, and it gets used. If
not, this one gets used.
friends are not inherited, so this idea won't work. However, you may cleverly use macro instead for example:
#define GEN(X) \
friend bool operator> (T const &lhs, T const &rhs) { return rhs < lhs; } \
friend bool operator==(T const &lhs, T const &rhs) { return !(lhs < rhs || lhs > rhs); } \
friend bool operator!=(T const &lhs, T const &rhs) { return !(rhs == lhs); } \
friend bool operator<=(T const &lhs, T const &rhs) { return !(rhs < lhs); } \
friend bool operator>=(T const &lhs, T const &rhs) { return !(lhs < rhs); }
And you may use as:
class Foo
{
...
GEN(foo)
};

std::rel_ops functionality as a base class. Is that appropriate solution?

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?