c++ less operator overload, which way to use?
Solution 1
They are essentially the same, other than the first being non-const and allowing you to modify itself.
I prefer the second for 2 reasons:
- It doesn't have to be a
friend
. -
lhs
does not have to be aRecord
Solution 2
The best way to define the less operator is:
struct Record{
(...)
const bool operator < ( const Record &r ) const{
return ( num < r.num );
}
};
Solution 3
Welcome to c++20 where we have even more options.
//version 1
bool operator <(const Record& rhs)
{
return this->num>rhs.num;
}
this one is wrong, it should read:
//version 1
bool operator <(const Record& rhs)const
{
return this->num>rhs.num;
}
as you want the left hand side to be const-qualified as well.
//version 2
friend bool operator <(const Record& lhs, const Record& rhs) //friend claim has to be here
{
return lhs->num>rhs->num;
}
this one is symmetric. So suppose you have a struct Bar
with an operator Record
.
Then
Record rhs;
Bar lhs;
assert( lhs < bar );
the above works with a symmetric case, but not with a member function version.
The friend
in class version is an operator that can only be found via Koenig lookup (Argument Dependent Lookup). This makes it very useful for when you want a symmetric operator (or one where the type is on the right, like ostream&<<*this
) bound to a specific template class instance.
If it is outside of the class, it has to be template function, and a template function does overloading differently than a non-template function does; non-template functions permit conversion.
template<class T>
struct point {
T x ,y;
point operator-(point const& rhs)const{
return {x-rhs.x,y-rhs.y};
}
friend point operator+(point const& lhs, point const& rhs) {
return {lhs.x+rhs.x, lhs.y+rhs.y};
}
};
template<class T>
point<T> operator*( point<T> const& lhs, point<T> const& rhs ) {
return {lhs.x*rhs.x, lhs.y*rhs.y};
}
here -
is asymmetric, so if we have a type that converts to a point<int>
on the left, -
won't be found.
+
is symmetric and a "Koenig operator", so it is a non-template operator.
*
is symmetric, but is a template operator. If you have something that converts-to-point, it won't find the *
overload, because deduction will fail.
//version 3
inline bool operator <(const Record& lhs, const Record& rhs)
{
return lhs->num>rhs->num;
}
this is similar to the template
above, but here that problem doesn't occur. The difference here is that you can get the address of this function outside of the class, while the "koenig operator<
" you wrote can only be found via ADL. Oh, and this isn't a friend.
c++17 adds in
auto operator<=>(const Record&)=default;
where we use the spaceship operator <=>
to define ordering automatically.
This will use the ordering of both c
and num
to produce the required result.
Much like the rule of 5, you should seek to make =default
here work correctly. Having state that <
ignores is a bad smell, and so is entangling different parts of your state.
Related videos on Youtube
user268451
Updated on April 27, 2021Comments
-
user268451 about 3 years
For example: in a C++ header file, if I defined a
struct Record
and I would like to use it for possible sorting so that I want to overload theless operator
. Here are three ways I noticed in various code. I roughly noticed that: if I'm going to putRecord
into astd::set
,map
,priority_queue
, … containers, the version 2 works (probably version 3 as well); if I'm going to saveRecord
into avector<Record> v
and then callmake_heap(v.begin(), v.end())
etc.. then only version 1 works.struct Record { char c; int num; //version 1 bool operator <(const Record& rhs) { return this->num>rhs.num; } //version 2 friend bool operator <(const Record& lhs, const Record& rhs) //friend claim has to be here { return lhs->num>rhs->num; } };
in the same header file for example:
//version 3 inline bool operator <(const Record& lhs, const Record& rhs) { return lhs->num>rhs->num; }
Basically, I would like to throw the questions here to see if someone could come up with some summary what's the differences among these three methods and what are the right places for each version?
-
Cheers and hth. - Alf over 12 yearscould you please post complete example programs for each case that Does Not Work
-
Mooing Duck over 12 yearsideone.com/issBj all scenarios work on this compiler
-
user268451 over 12 yearssorry, I didn't finish editing the question first. Now,it should be complete
-
-
K-ballo over 12 yearsActually one should always favour out-of-class, so that conversions on both left and right types apply (and not just the right one). If possible to make it a non-friend then even better yet, since it decouples the operator from the class implementing it just in terms of the public interface.
-
user268451 over 12 yearsI think the 2nd version, if you claim the operator < inside the class definition, you have to claim it as a friend of the class
-
Pubby over 12 years@user268451 It doesn't have to be a friend. It actually is best if it isn't one, as it leads to better encapsulation.
-
vpalmu over 12 yearsYou don't want conversions on comparison operators unless you're some kind of number!
-
Sjoerd over 12 yearsThis does not allow conversions on the left hand side, so definitely is not the best way.
-
Sjoerd over 12 years@Pubby8 If you want to define a free function from inside a class, you'll have to add
friend
. This has nothing to do with accessing the private members. -
Kashyap over 12 years@Joshua that's not always true, if some container (map, sorted-set, ...) needs it you might implement it for "non-number" classes.
-
vpalmu over 12 yearsReally, convert one type of collection to another implicitly?
-
g24l over 9 yearsThe path of ipmlicit conversions is dangerous and thy shall not walk