Less than operator cannot be overloaded as member function - c++

I want to use my class as key in a map, so I overload operator+. It works well if I overload it as a friend function. When I overload it as a member function inside the class, it causes a compile error.
error C2678: binary '<': no operator found which takes a left-hand operand of type 'const Syl' (or there is no acceptable conversion)'.
In details, this doesn't compile, and generate the compile error:
Syl.h
bool operator< (const Syl& rhs);
Syl.cpp
bool Syl::operator< (const Syl& rhs) { return false; }
While this compiles.
Syl.h
friend bool operator< (const Syl& lhs, const Syl& rhs);
Syl.cpp
bool operator< (const Syl& lhs, const Syl& rhs) { return false; }
I don't know why. I know that operator< is binary, but is there anyway to overload it as function member?

Typically, member operators such as operator< do not modify the object they operate on. If that is the case you need to specify that the method is constant by putting the keyword const at the end of the declaration, i.e.
class Syl {
...
public:
bool operator<(const Syl& rhs) const;
}
An operator like that can be used with STL containers such as std::map.
Your current version of the member operator, when translated into a stand-alone operator would look as:
friend bool operator<(Syl& lhs, const Syl& rhs);
Notice the lack of const for lhs. It is still correct per se, but atypical. You need to provide an l-value as lhs. If you provide something else you get the ``no operator found...'' error. STL std::map does not expect that either, hence your error, probably originating from standard headers, somewhere deep in the template implementation.

Assuming a and b are both of type Syl, your first (member form) is not valid in an expression a < b if a is const, and will result in a compilation error.
To fix that, you need to specify the member operator<() as
bool operator< (const Syl& rhs) const; // note the trailing const
Without that trailing const, in an expression a < b, a cannot be const (i.e. the operator<() is permitted to change it).
Your second form is correct, since it specifies that both operands are const references.
Comparison operators like < don't generally change EITHER of their operands. The const qualifiers communicate that fact.
Bear in mind that you can provide the member form OR the non-member form. Providing both will cause an error due to ambiguity (the compiler has no basis to prefer one over the other, when it sees an expression like a < b).

Related

Remove global implicit function - avoid ambiguous operator

Let's say I have an enum class:
enum class PType : int
{
Parallel = 0,
AntiParallel = 1,
BiParallel = 2,
NotParallel = 3
};
I now want to create the two operators
bool operator==(PType lhs, PType&& rhs)
bool operator==(PType&& lhs, PType rhs)
so that I can find out on which side of the == my rvalue reference is placed.
Unfortunately I get a compiler error
error C2593: 'operator ==' is ambiguous
in VisualStudio because the compiler automatically generates the following operator:
bool operator==(const PType lhs, const PType rhs);
for it. Trying to delete it with
bool operator==(const PType lhs, const PType rhs) = delete;
doesn't result in an compilation error in that line but I still get the "is ambiguous" error afterward.
Any ideas on how to make this work?
EDIT:
I know how to do it with a regular class but I still want to figure out why the compiler produces the error. I know the code is sketchy, bad style and could lead to errors as mentioned by many.
Anyway, I had the idea that this type of thing could work and so I tried and I think it can still work if I only could get "= delete" to work.
So now I ask this question out of pure academic interest because I want to learn more about the compiler and why the compiler reports no error when I delete the function but then complains a few lines later that the allegedly deleted function is considered an overload candidate.
The reason why I want to differentiate between where the rvalue reference is because then I know on which side of the == the variable is and on which side the compare value like PType::BiParallel can be found. The reason for this is that a variable containing Parallel or AntiParallel compared with PType::BiParallel should return true as I just want to know if we have some sort of parallelism. On the other hand if the variable contains BiParallel and is compared against PType::Parallel or PType::AntiParallel both comparisons should be false because you can not say which one is true.
Again, I know this is bad style but I find it academically interesting why the compiler is accepting the "= delete".
I had written another answer to this question, but the OP pointed out that my answer was incorrect. Upon thinking about this more, I believe that what the OP is trying to do is simply not possible.
As described in C++17 [over.built]/16, during overload resolution, a built-in candidate is generated with the signature
bool operator==(PType, PType);
If the built-in candidate is selected, then the built-in semantics of == will apply.
Now, it is possible to define your own operator==, but unless you declare one with the same exact signature as the built-in candidate, the built-in candidate will still get generated. Thus, at overload resolution time, the built-in candidate either wins (due to exact match), or is tied with some user-declared overload, resultin in ambiguity.
If you do declare your own operator== with the exact signature of the built-in candidate, then the built-in candidate is not generated ([over.match.oper]/(3.3.4)). You are allowed to delete it if you wish:
bool operator==(PType, PType) = delete;
However, a deleted function still participates in overload resolution (unless it is a defaulted move constructor or move assignment operator). Therefore, this does not solve the problem: with additional overloads such as
bool operator==(PType&&, PType);
there is potential for ambiguity, and such ambiguity only arises once the argument types are actually known. That's why the compilation error doesn't occur until you actually try to use ==.
What you really want is some way to prevent the built-in candidate, or another function with the same signature, from being considered by overload resolution at all. If you could do so, then you could force an overload to be chosen based on value category from an overload set like the following:
bool operator==(const PType& lhs, const PType& rhs);
bool operator==(const PType& lhs, PType&& rhs);
bool operator==(PType&& lhs, const PType& rhs);
bool operator==(PType&& lhs, PType&& rhs);
But as far as I can see, there is no way to do this, so what the OP wants does not seem possible.

Overloading Operators Twice With Different Arguments

I want to overload the * operator for a class, one which is in case it is multiplied by a real number double and the other is when multiplied by the same class type.
Below is my code:
class Vector2D {
...
friend Vector2D operator* (const Vector2D&);
friend Vector2D operator* (const double); // getting error here
};
The error I'm getting is: 'Vector2D operator*(double)' must have an argument of class or enumerated type
Is it possible to overload operators more than once, and if so, why am I getting an error?
class Vector2D {
...
friend Vector2D operator* (const Vector2D&);
That is overloading unary operator* (i.e. dereference operator) as a free function, not overloading the multiplication of two vectors. You cannot overload operators that apply only to non-class types, and that is the error for:
friend Vector2D operator* (const double);
You probably meant to write:
friend Vector2D operator*(const Vector2D&,const Vector2D&);
friend Vector2D operator*(const Vector2D&,double);
The problem is that you're overloading unary operator*, not binary. Your friend declarations are not member functions.
Either remove the friend (if you want to overload the operators as member functions), or else add a first parameter of type const Vector2D& or Vector2D& (if you want to overload as free functions). The latter is usually preferable, since then you get implicit conversion on both arguments -- member operator overloads will implicitly convert the right hand side but not the left.
The reason you might not want a reference as the first argument to binary operator*, is so that you can implement it like this:
Vector2D operator*(Vector2D lhs, const Vector2D &rhs) {
lhs *= rhs;
return lhs;
}
The same would apply to addition (and addition of two vectors makes sense to me in a way that multiplication of two vectors does not).
You are not allowed to override the operators for built-in types. In this case you're providing a declaration for a unary * operator for type double which isn't allowed.
I think you want to remove the friend declaration and make these member functions instead.

What operators should be declared as friends?

In some books and often around the internet I see recommendations like "operator== should be declared as friend".
How should I understand when an operator must be declared as friend and when it should be declared as member? What are the operators that will most often need to be declared as friends besides == and <<?
This really depends on whether a class is going to be on the left- or right-hand side of the call to operator== (or other operator). If a class is going to be on the right-hand side of the expression—and does not provide an implicit conversion to a type that can be compared with the left-hand side—you need to implement operator== as a separate function or as a friend of the class. If the operator needs to access private class data, it must be declared as a friend.
For example,
class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
};
allows you to compare a message to a string
Message message("Test");
std::string msg("Test");
if (message == msg) {
// do stuff...
}
but not the other way around
if (msg == message) { // this won't compile
You need to declare a friend operator== inside the class
class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
friend bool operator==(const std::string& lhs, const Message& rhs);
};
or declare an implicit conversion operator to the appropriate type
class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
operator std::string() const;
};
or declare a separate function, which doesn't need to be a friend if it doesn't access private class data
bool operator==(const std::string& lhs, const Message& rhs);
When you have your operators outside the class, both parameters can participate in implicit type conversions (whereas with operators being defined in the class body, only the right-hand operands can). Generally, that's a benefit for all the classic binary operators (i.e. ==,!=, +, -, <<, ... ).
Of course you should only declare operators friends of your class if you need to and not if they compute their result solely based on public members of the class.
Generally, only operators which are implemented as free functions that genuinely need to access to private or protected data of the class that they operate on should be declared as friends, otherwise they should just be non-friend non-member functions.
Generally, the only operators that I implement as member functions are those that are fundamentally asymmetric and where the operands don't have equivalent roles. The ones that I tend to implement as members are those required to be members: simple assignment, (), [] and -> together with compound assignment operators, unary operators and perhaps some overloads of << and >> for classes that are themselves stream or stream-like classes. I never overload &&, || or ,.
All other operators I tend to implement as free functions, preferably using the public interface of the classes which they operate on, falling back to being friends only where necessary.
Keeping operators such as !=, ==, <, +, /, etc as non-member functions enables identical treatment of the left and right operands with respect to implicit conversion sequences which helps to reduce the number of surprising asymmetries.
No one has mentioned hidden friends idiom which I suspect is what the books mean.
Long version: https://www.justsoftwaresolutions.co.uk/cplusplus/hidden-friends.html
Short version:
Operators are found via ADL (argument dependent lookup) most of the time. This is how an operator== defined for std::string in std is found when you are not in namespace std.
One of the problems, common for operators is a gigantic overload set. You can often see this in error messages if you try to use operator<< for something that is not printable.
So - if you declare operator== in the namespace containing the class directly, it will work but it will also participate in all of the overload resolutions in that namespace which is going to slow down your compilation and give you more noise in the errors.
Introducing hidden friends:
struct X {
friend bool operator==(const X& x, const X& y) {...}
};
This operator== will only be considered for overload resolution if one of the operands has type X. In all other cases it will not be seen, so your compilation will be faster and your error messages better.
Same goes for all two operand operators, like operator<< and also other functions intended for ADL, like swap.
I always define my operators like this and it's considered a good practise by quite a few people nowadays.
The only downside is - there is no very good way to define it out of line. You might want to consider dispatching to some private function.
Or you can do this: https://godbolt.org/z/hMarb4 - but it means that at least in one cpp file the operator== will participate in normal lookup.

Why can't we overload "=" using friend function?

Why it is not allowed to overload "=" using friend function?
I have written a small program but it is giving error.
class comp
{
int real;
int imaginary;
public:
comp(){real=0; imaginary=0;}
void show(){cout << "Real="<<real<<" Imaginary="<<imaginary<<endl;}
void set(int i,int j){real=i;imaginary=j;}
friend comp operator=(comp &op1,const comp &op2);
};
comp operator=(comp &op1,const comp &op2)
{
op1.imaginary=op2.imaginary;
op1.real=op2.real;
return op1;
}
int main()
{
comp a,b;
a.set(10,20);
b=a;
b.show();
return 0;
}
The compilation gives the following error :-
[root#dogmatix stackoverflow]# g++ prog4.cpp
prog4.cpp:11: error: ‘comp operator=(comp&, const comp&)’ must be a nonstatic member function
prog4.cpp:14: error: ‘comp operator=(comp&, const comp&)’ must be a nonstatic member function
prog4.cpp: In function ‘int main()’:
prog4.cpp:25: error: ambiguous overload for ‘operator=’ in ‘b = a’
prog4.cpp:4: note: candidates are: comp& comp::operator=(const comp&)
prog4.cpp:14: note: comp operator=(comp&, const comp&)
Because if you do not declare it as a class member compiler will make one up for you and it will introduce ambiguity.
The assignment operator is explicitly required to be a class member operator. That is a sufficient reason for the compiler to fail to compile your code. Assignment is one of the special member functions defined in the standard (like the copy constructor) that will be generated by the compiler if you do not provide your own.
Unlike other operations that can be understood as external to the left hand side operator, the assignment is an operation that is semantically bound to the left hand side: modify this instance to be equal to the right hand side instance (by some definition of equal), so it makes sense to have it as an operation of the class and not an external operation. On the other hand, other operators as addition are not bound to a particular instance: is a+b an operation of a or b or none of them? -- a and b are used in the operation, but the operation acts on the result value that is returned.
That approach is actually recommended and used: define operator+= (that applies to the instance) as a member function, and then implement operator+ as a free function that operates on the result:
struct example {
example& operator+=( const example& rhs );
};
example operator+( const example& lhs, const example& rhs ) {
example ret( lhs );
ret += rhs;
return ret;
}
// usually implemented as:
// example operator+( example lhs, const example& rhs ) {
// return lhs += rhs; // note that lhs is actually a copy, not the real lhs
//}
Assignment(=) operator is a special operator that will be provided by the constructor to the class when programmer has not provided(overloaded) as member of the class.(like copy constructor).
When programmer is overloading = operator using friend function, two = operations will exists:
1) compiler is providing = operator
2) programmer is providing(overloading) = operator by friend function.
Then simply ambiguity will be created and compiler will gives error. Its compilation error.
There is no good reason for that, I think Stepanov proposed that there should be a free operator= and many good stuff can be done with that (even more than what can be done with the move assignment). I can't find the citation but Stepanov went as a far as to suggest that the constructors could be free functions http://www.stlport.org/resources/StepanovUSA.html.
There is a way around it, which is to systematically declare a template<class Other> A& operator=(Other const& t); in inside all your classes, in this way you leave the option to anyone to define a custom assignment operator.
Of course you can't do this with a class you don't have control over.
Having said that it is nowadays not that bad since we have move assignment. And in some sense conversion operators B::operator A() const{...} are almost like a custom copy assignment. Conversion operators are now usable because of explicit. However you have to have control over the second type (B), the right-hand type in assignment.
Next question is why conversion operator need to be members them selves? Again, I don't think there is a good reason.

C++ template class error with operator ==

Error:
error C2678: binary '==' : no operator found which takes a left-hand operand of type 'const entry' (or there is no acceptable conversion)
The function:
template <class T, int maxSize>
int indexList<T, maxSize>::search(const T& target) const
{
for (int i = 0; i < maxSize; i++)
if (elements[i] == target) //ERROR???
return i; // target found at position i
// target not found
return -1;
}
indexList.h
indexList.cpp
Is this suppose to be an overloaded operator? Being a template class I am not sure I understand the error?
Solution-
The overload function in the class now declared const:
//Operators
bool entry::operator == (const entry& dE) const <--
{
return (name ==dE.name);
}
Start by reading the error text exactly as it is:
binary '==' : no operator found which takes a left-hand operand of type 'const entry'
It means it can't find any == operator that accepts an entry type as its left operand. This code isn't valid:
entry const e;
if (e == foo)
You've showed us the code for your list class, but that's not what the error is about. The error is about a lack of operators for the entry type, whatever that is. Either give the class an operator== function, or declare a standalone operator== function that accepts a const entry& as its first parameter.
struct entry {
bool operator==(const entry& other) const;
};
// or
bool operator==(const entry& lhs, const entry& rhs);
I think the latter is the preferred style.
The problem refers to the type T that is being used in this instance not having an operator== defined. I would guess from your question that this is a class 'entry'.
It might also be that the 'entry' class does not have the operator== defined correctly to take a const entry& as a parameter.
This is likely "const poisoning", where your use of const in the declaration of your search function will force you to add const versions of all the downstream functions you call.
In a function declared const, the this pointer is considered const, which means that all the members you use through that pointer are considered const as well. If your operator == () for whatever type T you are specializing with doesn't explicitly specify const parameters, you will get this error.
If you can't ensure that all Ts you use will have the proper operator == () calls, I'd remove the const specifiers on the member function templates.
The type T you're using as a parameter to this class should have an operator==() since the code you give doesn't contain the instantiation of the template its difficult to know what's wrong.
On a different note, the function definitions of a template should be in the .h file along with the class or else the compiler would not be able to instantiate it properly.
The equality operator for user-defined types is not defined by default. This doesn't have anything to do with your template class, but rather your struct or class "entry".
Therefore in you'll have to override the equality operator in struct entry or class entry.
Alternatively, if you don't want to force everything that uses that template to define an equality operator, you can modify the template interface to accept a Comparator that does the equality comparison.
Sometimes it is quite enough to write
...
if (target == elements[i])
...