c++ less operator overload, which way to use? - c++

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 the less operator. Here are three ways I noticed in various code. I roughly noticed that: if I'm going to put Record into a std::set, map, priority_queue, … containers, the version 2 works (probably version 3 as well); if I'm going to save Record into a vector<Record> v and then call make_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?

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 a Record

The best way to define the less operator is:
struct Record{
(...)
const bool operator < ( const Record &r ) const{
return ( num < r.num );
}
};

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.

The non-member equivalent of your member function
bool operator <(const Record& rhs);
is
bool operator <(Record& lhs, const Record& rhs); // lhs is non-const
Now STL containers treat the items they store as const (at least as long as the comparison operator is concerned). So they call const-const variant of your operator. If they don't find it (in case you provided variant 1 only) - it is a compile error.
If you provide both const-const member and const-const non-member:
struct Record
{
bool operator <(const Record& rhs) const;
};
bool operator <(Record& lhs, const Record& rhs);
it is yet another compiler error because such definition leads to an ambiguity:
If two matches are found at the highest level where a match is found,
the call is rejected as ambiguous. /Stroustrup, C++, section 12.3.1/
Finally, (as noted in the previous answers) there's no need in the friend modifier since by default all fields of the struct are public.
PS make_heap doesn't expect the compared items to be const because it is a more low-level beast and using it you're kind of co-authoring a new heap-based library so it is your responsibility to track const-ness of items.
PPS set treatment of items as const does not protect you from modifying the keys of the items after they are inserted into the container - it will result in a runtime error (segfault) if you try it.

Favor in-class unless it cannot be in-class because first argument is the wrong type.

Related

Sort vector of objects by value of member [duplicate]

For an assignment students have to make a Card struct that keeps the Suit, Rank and Bitmap of a Card. This struct needs an overloaded "<" operator to compare whether the lhs Card's Rank is smaller than the rhs Card and return the bool. So far this is my Card.h file:
#pragma once
#include "GameEngine.h"
struct Card
{
public:
//Constructor and Destructor
Card();
virtual ~Card();
//Methods
bool operator< (const Card& lhs, const Card& rhs)
{
return (lhs.m_Rank < rhs.m_Rank);
}
//Enumerations
enum class Suit
{
Diamonds,
Clubs,
Hearts,
Spades,
};
enum class Rank
{
RankAce,
RankTwo,
RankThree,
RankFour,
RankFive,
RankSix,
RankSeven,
RankEight,
RankNine,
RankTen,
RankJack,
RankQueen,
RankKing,
};
private:
//Datamembers
Bitmap *m_BmpPtr;
Rank m_Rank;
Suit m_Suit;
};
However the operator< overload claims that it has too many parameters. Isn't this the right way to make sure both lhs and rhs can be compared in one overload? It's not like I have to split it up right?
Many thanks in advance.
The compiler think this is a member function, but member function operators cannot have more than one argument. The first argument is implicitly *this while the second is the one you supply.
You can make this a member function by stripping the first argument and using *this in place of lhs. Otherwise, you can use an idiomatic solution and make it a friend:
friend bool operator< (const Card& lhs, const Card& rhs)
{
return lhs.m_Rank < rhs.m_Rank;
}
You have declared bool operator< as a member function. Member functions have an implicit first parameter for this, so your operator really expects three parameters. You can solve this by using a non-member (friend in your case - remember that friend functions are non-member functions):
friend
bool operator< (const Card& lhs, const Card& rhs)
{
return (lhs.m_Rank < rhs.m_Rank);
}
or using a member with a single rhs parameter:
bool operator< (const Card& rhs) const
{
return (m_Rank < rhs.m_Rank);
}

What is meant by non-member operator overloading

I was recently going over an article on operator overloading in which it mentioned non-member operator overloading. I would appreciate it if someone could explain what is meant by non-member operator overloading with an example. I know what member operator overloading is (A method in a class which overloads an operator type (binary..etc) . I came across this post on SO which makes me believe that the purpose of non-member operator overloading is to handle operator overloading in which the first parameter is a not a class and is simply a native type. Any links or examples that explain what non-member operator overloading is would definitely be appreciated.
It means you can overload out-of-class:
struct X { int data; };
bool operator<(X const& a, X const& b)
{
return a.data < b.data;
}
This is useful for assymetrical overloading, where the left operand doesn't need to be your own type:
bool operator<(int a, X const& b)
{
return a < b.data;
}
A common idiom here is to combine it with in-class definition and friend declaration:
struct X
{
int data;
friend bool operator<(X const& a, X const& b) { return a.data<b.data; }
friend bool operator<(int a, X const& b) { return a<b.data; }
};
Here, operator< is still technically non-member.
Another useful side-effect of this, as pointed out by DrewDormann below, is that the (X const&, X const&) will apply to any operands that are implicitly convertible to X const&, not just expressions of that exact type.
the most common way is to overload operator<< that will be called on std::cout:
namespace X {
class MyClass {
...
};
}
std::ostream& operator<< (std::ostream&, const X::MyClass&);
this is called on std::ostream member so you don't define it inside your class.
however sometimes the functionality cannot be achieved via the public interfaces (because your operator needs access to data representation).

Overloading += operator in Polynom class for adding both Polynoms and constants

I am trying to overload += operator for my template Polynom class in a way so I would be able to use both Polynoms and constants as argument.
I wrote a constructor and following operator inside my class:
Polynom(const T& num = 0) {
coefs.push_back(num);
}
friend Polynom& operator += (Polynom& lhs, const Polynom& rhs) {
...
}
And it works fine, I am able to use: poly += 1;. When compiler runs into something likes that what does it do? It sees that there is no += operator that uses these arguments:
(Polynom<int>& lhs, const int)
But there is one for:
(Polynom<int>& lhs, const Polynom& rhs)
So, it tries to convert const int to const Polynom&? And it uses constructor for that, right? But then why doesn't this declaration work when adding a constant:
Polynom& operator += (Polynom& rhs) {
...
}
Compiler says "no match for operator +=".
When passing an int to a function taking a const Polynom&, the compiler is able to construct a temporary Polynom object from the int that is then bound to the const Polynom& parameter. However this doesn't happen with the Polynom& parameter because temporaries cannot be bound to non const references.
You need to show us the template code or your smallest compilable code that demonstrates the issue.
Try creating a method that accepts an integer parameter:
friend Polynom& operator+=(const Polynom& lhs, int constant);
I'm confused about your notation Polynom<int> which indicates your Polynom class is a template.

Right way to overload < operator?

I have a class called user which has a lname field. Is this the right way to overload the "<" operator?
bool User::operator<(const User& other)
{
std::cout << "< operator was called" << std::endl;
if (this != &other)
{
if (lname.compare(other.lname) == 0)
{
return true;
}
}
return false;
}
I am trying to use this in a more complicated set of things and it is failing - just want to make sure this much is right.
As others have pointed out, your operator< doesn't allow the left side to be const. Changing the function signature to
bool User::operator<(const User& other) const
is an improvement. But I would actually recommend making it a non-member function instead:
class User {
public:
friend bool operator<(const User& u1, const User& u2);
// ...
};
bool operator<(const User& u1, const User& u2)
{
// ...
}
For one thing, it's a little more legible in my opinion.
But also, it sometimes makes a technical difference. With a non-member function, the expression a < b attempts implicit conversions on both a and b to see if your operator< is a viable overload. But with a member function, implicit conversions can apply to b, but not to a: a must be of type User or a derived type. This can lead to surprising situations where a < b compiles but b < a doesn't.
It seems better to me to hide the 'lname' field as private.
return lname.compare(other.getName()) < 0;
Try:
bool User::operator<(const User& other) const
{
return lname.compare(other.lname) < 0;
}
The correct way to implement operator< is as a const-function:
bool User::operator<( const User& other ) const
This means that the function does not modify its members and thus can be called on const instances of your class.

operator overloading in c++

suppose i have 2 objects of a class and it has one int data member.
i want to add those integer data to other object and store the output in the first obj's data member.I can overload the + operator and use the statement like below
X+Y //where X and Y are objects of one class.
if i have to add like below
X+10// here i want to add 10 to the data member of X.
for above also i can overload the operator +.
but if i have 10+X and i want to add 10 to the data member of X how could i do it?
The same way:
MyClass operator+(MyClass const& lhs, MyClass const& rhs);
MyClass operator+(MyClass const& lhs, int rhs);
MyClass operator+(int lhs, MyClass const& rhs);
(operator+ should not normally be a member.)
If you overload operator+, you'll also want to overload +=. One
frequent idiom involved implementing + in terms of +=. This can
be more or less automated (if you have a lot of classes
supporting operators) by defining something like:
template<typename DerivedType>
class ArithmeticOperators
{
public:
friend DerivedType operator+(
DerivedType const& lhs,
DerivedType const& rhs)
{
DerivedType result(lhs);
result += rhs;
return result;
}
// And so on for the other operators...
protected:
~ArithmeticOperators() {}
};
template<typename DerivedType, typename OtherType>
class MixedArithmeticOperators
{
public:
friend DerivedType operator+(
DerivedType const& lhs,
OtherType const& rhs)
{
DerivedType result(lhs);
result += rhs;
return result;
}
friend DerivedType operator+(
OtherType const& lhs,
DerivedType const& rhs)
{
DerivedType result(rhs);
result += lsh;
return result;
}
// And so on: non-commutative operators only have the
// first.
protected:
~MixedArithmeticOperators() {}
};
, then deriving from whatever is needed: in your case:
class MyClass : public ArithmeticOperators<MyClass>,
MixedArithmeticOperators<MyClass, int>
You have to create an overloaded operator as a free function with the correct parameter order:
// This will match "int + YourClass" additions
YourClass operator+(int Left, const YourClass & Right)
{
// If your addition operation is commutative, you can just call the other
// version swapping the arguments, otherwise put here your addition logic
return Right + Left;
}
If the operator needs to fiddle with the internals of your class you can make it friend.
As others pointed out, there are some best/common practices that you should follow if you implement operator+, I suggest you to have a look to the great C++-FAQ on operator overloading for more info about them.
Don't overload the operator + as a member function of the class.
You can either define a global function operator + with two parameters or make operator + a friend of your class (In that case you should be having a parameterized constructor to convert 10 to an object of your class-type).
Define a non-member stand-alone free function as:
sample operator+(int leftOperand, const sample & rightOperand)
{
//...
}
Although you can do that using a global operator+, I would advise not to do it.
Only use operator overloading for data types for which the operators are immediately clear, e.g.:
complex numbers
strings (+,- ok, but * probably doesn't make much sense here)
The risk with overloaded operators is that the compiler may perform unwanted conversions, especially if you didn't make the single-argument constructor explicit.
You should define a non-member friend function
YourClass operator+(const YourClass &a, const YourClass&b) {
// do the math here
}
it should be friend to get to the private members of YourClass. Also you should create constructor for YourClass that takes int.
In this way you've got one operator+ and for every other then int you just create another constructor.