I recently noticed some operator overloading behaviour that I could not figure out myself. The following two classes only differ in the const on the member comparison operator overloads of ClassA. In ClassB they are not const. Generally I know that one would always prefer the const one, but still I am interested in why we see the behaviour I will describe below.
#include <string>
class ClassA {
public:
explicit ClassA(double t) : _t(t) {}
std::string operator<=(int const& other) const {
return "A(<=)";
}
std::string operator==(int const& other) const {
return "A(==)";
}
friend std::string operator<=(int const& other, ClassA const& expr) {
return "A'(<=)";
}
friend std::string operator==(int const& other, ClassA const& expr) {
return "A'(==)";
}
private:
double _t;
};
class ClassB {
public:
explicit ClassB(double t) : _t(t) {}
std::string operator<=(int const& other) {
return "B(<=)";
}
std::string operator==(int const& other) {
return "B(==)";
}
friend std::string operator<=(int const& other, ClassB const& expr) {
return "B'(<=)";
}
friend std::string operator==(int const& other, ClassB const& expr) {
return "B'(==)";
}
private:
double _t;
};
Now I want to use these classes and comparison functions in const and non-const scenarios.
int
main(int argc,
char* argv[]) {
ClassA a1{0};
1==a1; //OK
1<=a1; //OK
ClassA const a2{0};
1==a2; //OK
1<=a2; //OK
ClassB b1{0};
1==b1; //NOT OK
1<=b1; //OK
ClassB const b2{0};
1==b2; //OK
1<=b2; //OK
return 0;
}
Everything works fine but the one line that I marked NOT OK. This throws a compiler error.
error C2446: '==': no conversion from 'ClassB' to 'int'
My question splits up into three parts, but I would expect that there is one good reason that answers all of them. So I hope it is still fine to post this into one single SO question.
Why is the equality operator
==not compiling, when the inequality<=is? Why does it matter for the friend functions whether the member functions areconstor not? And why does making theClassBobjectconstfix it?
Updates:
- In the comments @Eljay pointed out that the problem is probably created by the a new C++20 feature that automatically generates comparison operators with inverted arguments. This apparently makes member
std::string operator==(int const& other)(after re-arrangement) a better match for the1==b1. After some digging I found the rule saying these should be generated in the rules for overload resolution.
- rewritten candidates:
- For the four relational operator expressions x<y, x<=y, x>y, and x>=y, all member, non-member, and built-in operator<=>'s found are added to the set.
- For the four relational operator expressions x<y, x<=y, x>y, and x>=y as well as the three-way comparison expression x<=>y, a synthesized candidate with the order of the two parameters reversed is added for each member, non-member, and built-in operator<=>'s found.
- For x!=y, all member, non-member, and built-in operator=='s found are added to the set.
- For equality operator expressions x==y and x!=y, a synthesized candidate with the order of the two parameters reversed is added for each member, non-member, and built-in operator=='s found.
In all cases, rewritten candidates are not considered in the context of the rewritten expression. For all other operators, the rewritten candidate set is empty.
@463035818_is_not_a_number pointed out some interesting finding about different compilers that can and can not compile the code in different versions. Specifically for
clangandgccwith the flag-std=c++2athe newest versionsx86-64 clang 12.0.0andx86-64 gcc 11.1do not compile, while older versionsx86-64 clang 9.0.1andx86-64 gcc 9.4do. For VisualStudio we see a similar pattern with the flag/std:c++latest. Here the newest versionx64 msvc v19.28 (VS16.9)does not compile, where the direct predecessorx64 msvc v19.28does. These test where made with the Compiler explorer godbolt.org.It is particularly interesting to note that the compiler errors of
clangandgccsuggest that the problem is thatstd::string operator==(int const& other)is not returning abool.
clang
error: return type 'std::string' (aka 'basic_string<char>') of selected 'operator==' function for rewritten '==' comparison is not 'bool'
1==b1; //NOT OK
gcc
error: return type of 'std::string ClassB::operator==(const int&)' is not 'bool'
1==b1; //NOT OK
While these are all very interesting insights the original question still remains open.