Remove global implicit function - avoid ambiguous operator - c++

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.

Related

How to write custom spaceship operator in c++20 [duplicate]

I'm running into a strange behavior with the new spaceship operator <=> in C++20. I'm using Visual Studio 2019 compiler with /std:c++latest.
This code compiles fine, as expected:
#include <compare>
struct X
{
int Dummy = 0;
auto operator<=>(const X&) const = default; // Default implementation
};
int main()
{
X a, b;
a == b; // OK!
return 0;
}
However, if I change X to this:
struct X
{
int Dummy = 0;
auto operator<=>(const X& other) const
{
return Dummy <=> other.Dummy;
}
};
I get the following compiler error:
error C2676: binary '==': 'X' does not define this operator or a conversion to a type acceptable to the predefined operator
I tried this on clang as well, and I get similar behavior.
I would appreciate some explanation on why the default implementation generates operator== correctly, but the custom one doesn't.
This is by design.
[class.compare.default] (emphasis mine)
3 If the class definition does not explicitly declare an ==
operator function, but declares a defaulted three-way comparison
operator function, an == operator function is declared implicitly
with the same access as the three-way comparison operator function.
The implicitly-declared == operator for a class X is an inline
member and is defined as defaulted in the definition of X.
Only a defaulted <=> allows a synthesized == to exist. The rationale is that classes like std::vector should not use a non-defaulted <=> for equality tests. Using <=> for == is not the most efficient way to compare vectors. <=> must give the exact ordering, whereas == may bail early by comparing sizes first.
If a class does something special in its three-way comparison, it will likely need to do something special in its ==. Thus, instead of generating a potentially non-sensible default, the language leaves it up to the programmer.
During the standardization of this feature, it was decided that equality and ordering should logically be separated. As such, uses of equality testing (== and !=) will never invoke operator<=>. However, it was still seen as useful to be able to default both of them with a single declaration. So if you default operator<=>, it was decided that you also meant to default operator== (unless you define it later or had defined it earlier).
As to why this decision was made, the basic reasoning goes like this. Consider std::string. Ordering of two strings is lexicographical; each character has its integer value compared against each character in the other string. The first inequality results in the result of ordering.
However, equality testing of strings has a short-circuit. If the two strings aren't of equal length, then there's no point in doing character-wise comparison at all; they aren't equal. So if someone is doing equality testing, you don't want to do it long-form if you can short-circuit it.
It turns out that many types that need a user-defined ordering will also offer some short-circuit mechanism for equality testing. To prevent people from implementing only operator<=> and throwing away potential performance, we effectively force everyone to do both.
The other answers explain really well why the language is like this. I just wanted to add that in case it's not obvious, it is of course possible to have a user-provided operator<=> with a defaulted operator==. You just need to explicitly write the defaulted operator==:
struct X
{
int Dummy = 0;
auto operator<=>(const X& other) const
{
return Dummy <=> other.Dummy;
}
bool operator==(const X& other) const = default;
};
Note that the defaulted operator== performs memberwise == comparisons. That is to say, it is not implemented in terms of the user-provided operator<=>. So requiring the programmer to explicitly ask for this is a minor safety feature to help prevent surprises.

Less than operator cannot be overloaded as member function

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

C++: force the compiler to use one of two competing operators

I've been playing with C++ recently, and I just stumbled upon an interesting precedence issue. I have one class with two operators: "cast to double" and "+". Like so:
class Weight {
double value_;
public:
explicit Weight(double value) : value_(value) {}
operator double() const { return value_; }
Weight operator+(const Weight& other) { return Weight(value_ + other.value_); }
};
When I try to add two instances of this class...
class Weighted {
Weight weight_;
public:
Weighted(const Weight& weight) : weight_(weight) {}
virtual Weighted twice() const {
Weight w = weight_ + weight_;
return Weighted(w);
}
};
...something unexpected happens: the compiler sees the "+" sign and casts the two weight_s to double. It then spits out a compilation error, because it can't implicitly cast the resulting double back to a Weight object, due to my explicit one-argument constructor.
The question: how can I tell the compiler to use my own Weight::operator+ to add the two objects, and to ignore the cast operator for this expression? Preferably without calling weight_.operator+(weight_), which defeats the purpose.
Update: Many thanks to chris for pointing out that the compiler is right not to use my class's operator+ because that operator is not const and the objects that are being +ed are const.
I now know of three ways to fix the above in VS2012. Do see the accepted answer from chris for additional information.
Add the explicit qualifier to Weight::operator double(). This
doesn't work in VS 2012 (no support), but it stands to reason that
it's a good solution for compilers that do accept this approach (from the accepted answer).
Remove the virtual qualifier from method Weighted::twice, but don't ask me why this works in VS.
Add the const qualifier to method Weight::operator+ (from the accepted answer).
Current version:
First of all, the virtual should have nothing to do with it. I'll bet that's a problem with MSVC, especially considering there's no difference on Clang. Anyway, your twice function is marked const. This means that members will be const Weight instead of Weight. This is a problem for operator+ because it only accepts a non-const this. Therefore, the only way the compiler can go is to convert them to double and add those.
Another problem is that adding explicit causes it to compile. In fact, this should remove the compiler's last resort of converting to double. This is indeed what happens on Clang:
error: invalid operands to binary expression ('const Weight' and 'const Weight')
Weight w = weight_ + weight_;
note: candidate function not viable: 'this' argument has type 'const Weight', but method is not marked const
Finally, making operator+ const (or a free function) is the correct solution. When you do this, you might think you'll add back this route and thus have another error from ambiguity between this and the double route, but Weight to const Weight & is a standard conversion, whereas Weight to double is a user-defined conversion, so the standard one is used and everything works.
As of the updated code in the question, this is fine. The reason it won't compile is the fault of MSVC. For reference, it does compile on Clang. It also compiles on MSVC12 and the 2013 CTP.
You may be storing the result in a Foo, but there is still an implicit conversion from double to Foo needed. You should return Foo(value_ + other.value_) in your addition operator so that the conversion is explicit. I recommend making the operator a free function as well because free operators are (almost) always at least as good as members. While I'm at it, a constructor initializer list would be a welcome change, too.
In addition, from C++11 onward, a generally preferred choice is to make your conversion operator explicit:
explicit operator double() const {return value_;}
Also note the const added in because no state is being changed.

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.

Why friend overloaded operator is preferred to conversion operator in this case

Hi I have a code like this, I think both the friend overloaded operator and conversion operator have the similar function. However, why does the friend overloaded operator is called in this case? What's the rules?
Thanks so much!
class A{
double i;
public:
A(int i):i(i) {}
operator double () const { cout<<"conversion operator"<<endl;return i;} // a conversion operator
friend bool operator>(int i, A a); // a friend funcion of operator >
};
bool operator>(int i, A a ){
cout<<"Friend"<<endl;
return i>a.i;
}
int main()
{
A aa(1);
if (0 > aa){
return 1;
}
}
No conversion is necessary for the overloaded operator> to be called. In order for the built-in operator> to be called, one conversion is necessary (the user-defined conversion operator. Overload resolution prefers options with fewer required conversions, so the overloaded operator> is used.
Note that if you were to change the definition of your overloaded operator> to be, for example:
friend bool operator>(double i, A a);
you would get a compilation error because both the overloaded operator> and the built-in operator> would require one conversion, and the compiler would not be able to resolve the ambiguity.
I am not claiming that my answer is supported by the standards, but lets think about it logically.
When you hit this line:
0 > aa
You have two options. Either you call the provided operator:
friend bool operator>(int i, A a);
Which is 100% compatible, or you can do two conversions to reach your destination! Which one would you choose?
If you add a conversion operator then an object of type A may be converted to double when you least expect it.
A good program does not provide a way for his classes to be accidently used and the conversion operator opens up the opertunity for the class to be used in a whole host of unintended situations (normally situations where you would expect a compile time error are not because of the auto type conversion).
As a result conversion operators (and single argument constructors) should be treateed with some care because of situations were the compiler may do conversion when you least expect it.
It is called because it is an exact match in the context of the expression 0 > aa. In fact, it is hard to figure out how you came up with the "why" question. By logic, one'd expect a "why" question if the friend weren't called in this case.
If you change the expression to 0.0 > aa, the call will beciome ambiguous, because neuther path will be better than the other.