Consider this valid C++17 example:
struct A {
bool operator==(const A&);
};
int main() {
return A{} == A{};
}
When compiled in clan
Does this warning mean that C++20 disallows using a typical comparison operator to compare two objects of the same type? What is the correct alternative? Is the situation expected to change in future drafts?
This isn't really a typical comparison operator, it's already kind of wrong - since it only allows a const
object on one side (your type A
wouldn't satisfy the new equality_comparable
concept either, even without any langauge changes).
You have to write it this way:
struct A {
bool operator==(const A&) const;
// ^^^^^^
};
This is the final rule for C++20.
The specific issue is that in C++20, comparison operators add a new notion of rewritten and reversed candidates. So lookup for the expression a == b
will also end up matching operators like b == a
. In the typical case, this means you have to write fewer operators, since we know equality is commutative.
But if you have a const-mismatch, what happens is you end up with these two candidates:
bool operator==(/* this*/ A&, A const&); // member function
bool operator==(A const&, /* this*/ A&); // reversed member function
With two arguments of type A
. The first candidate is better in the first argument, and the second candidate is better in the second argument. Neither candidate is better than the other, hence ambiguous.
It is a general rule of overload resolution that each argument type must be separately at least as close to the parameter type for a selected function as to the parameter type for any other:
struct A {A(int);};
void f(long,int); // #1
void f(int,A); // #2
void g() {f(0,0);} // error: ambiguous
The much worse conversion for the second argument for #2 doesn’t make up for the int
→long
conversion on the first argument.
In C++20, various rewrite rules have been added to obviate the need to write so many all-but-identical comparison operator overloads. While the trivial ambiguities between hand-written “reversed candidates” and identical compiler-generated ones are handled by tie-breaker rules that prefer real functions, that’s (again) not enough to make up for a worse conversion for any argument.
Comparison operators written carefully according to accepted (C++17) practices will very rarely run afoul of this, but questionable signatures like this (with asymmetric const
) may very well be problematic (in new ways). Hopefully more bugs are found than are caused by this extension.