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?
Related
Consider the following code snippet :
[[nodiscard]] bool operator==(const BasicIterator<const Type>& rhs) const noexcept {
if( this == &rhs ) {
return true;
}
return node_ == rhs.node_;
}
[[nodiscard]] bool operator==(const BasicIterator<Type>& rhs) const noexcept {
// how to call existing operator== implementation ??
//return operator==( rhs );
}
How should I use the same implementation for both operator==? Is it possible to call operator== <const Type> version from operator== <Type>?
Is there any cast for template types in this case?
Is Type a template? If not, maybe make it a template?
template <typename T>
[[nodiscard]] bool operator==(const BasicIterator<T>& rhs) const noexcept {
if( this == &rhs ) {
return true;
}
return node_ == rhs.node_;
}
If you do not want to expose this template to users, hide it somewhere and use in implementation:
private:
template <typename T>
[[nodiscard]] bool operator==(const BasicIterator<T>& lhs, const BasicIterator<T>& rhs) const noexcept {
if( &lhs== &rhs ) {
return true;
}
return lhs.node_ == rhs.node_;
}
public:
[[nodiscard]] bool operator==(const BasicIterator<const Type>& rhs) const noexcept {
return operator==<const Type>(*this, rhs);
}
[[nodiscard]] bool operator==(const BasicIterator<Type>& rhs) const noexcept {
return operator==<Type>(*this, rhs);
}
I struggle with a std::set that should support custom compare operations. My goal would be to be able to call find with just the key argument, without first creating an object of the set type. Something like this:
#include <set>
#include <cstdint>
#include <vector>
class TestItem {
public:
std::uint64_t id; // key
std::vector<double> areas; // some other data
};
struct TestItemCompare {
bool operator()(const std::uint64_t& lhs, const std::uint64_t& rhs) {
return lhs < rhs;
}
bool operator()(const std::uint64_t& lhs, const TestItem& rhs) {
return lhs < rhs.id;
}
bool operator()(const TestItem& lhs, const std::uint64_t& rhs) {
return lhs.id < rhs;
}
bool operator()(const TestItem& lhs, const TestItem& rhs) {
return lhs.id < rhs.id;
}
};
int main() {
std::set<TestItem, TestItemCompare> store;
std::uint64_t id = 0;
TestItem t;
t.id = 0;
auto it1 = store.find(t); // compiles
auto it2 = store.find(id); // fails to compile
return 0;
}
I thought this was possible...
It is possible. But your comparator must be a transparent one. And the member functions in functor need to be const
struct TestItemCompare {
using is_transparent = int; // actually any alias will do, void, custom type
bool operator()(const std::uint64_t& lhs, const std::uint64_t& rhs) const {
return lhs < rhs;
}
bool operator()(const std::uint64_t& lhs, const TestItem& rhs) const {
return lhs < rhs.id;
}
bool operator()(const TestItem& lhs, const std::uint64_t& rhs) const {
return lhs.id < rhs;
}
bool operator()(const TestItem& lhs, const TestItem& rhs) const {
return lhs.id < rhs.id;
}
};
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.
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)
};