Why is this code ambiguous starting with C++20?
template <typename T>
struct base_widget {
bool operator==(T const&) const;
};
struct gadget : base_widget<gadget> {};
bool foo(gadget const& a, gadget const& b) {
return a == b;
}
for MSVC that is an error:
error C2666: 'base_widget<gadget>::operator ==': overloaded functions have similar conversions
could be 'bool base_widget<gadget>::operator ==(const T &) const'
with
[
T=gadget
]
or 'bool base_widget<gadget>::operator ==(const T &) const' [synthesized expression 'y == x']
with
[
T=gadget
]
while trying to match the argument list '(const gadget, const gadget)'
for GCC it is a warning:
In function 'bool foo(const gadget&, const gadget&)':
warning: C++20 says that these are ambiguous, even though the second is reversed:
| return a == b;
| ^
note: candidate 1: 'bool base_widget<T>::operator==(const T&) const [with T = gadget]'
| bool operator==(T const&) const;
| ^~~~~~~~
note: candidate 2: 'bool base_widget<T>::operator==(const T&) const [with T = gadget]' (reversed)
And also a warning for Clang:
warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'const gadget' and 'const gadget') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
| return a == b;
| ~ ^ ~
note: ambiguity is between a regular call to this operator and a call with the argument order reversed
| bool operator==(T const&) const;
|
I know that C++20 allows this:
Overload resolution/operators in expressions
[over.match.oper]
For the equality operators, the rewritten candidates also include a synthesized candidate, with the order of the two parameters reversed, for each non-rewritten candidate for the expression y == x.
But why is the synthesized reversed operator problematic in this case?
Making the operator==
a friend
fixes the issue:
template <typename T>
struct base_widget {
friend bool operator==(T const&, T const&);
};
Why there is no issue with reversing in this case?
To demonstrate the problem more clearly, I created a simpler example: without templates and with non-member operator==
:
struct B{};
struct D : B{};
constexpr bool operator==(const B&, const D&)
{
return true;
}
static_assert( D{}==D{});
clang gives the following warning:
<source>:8:19: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'D' and 'D') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
8 | static_assert( D{}==D{});
| ~~~^ ~~~
<source>:4:16: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
4 | constexpr bool operator==(const B&, const D&)
|
In other words, the compiler cannot decide whether to convert the 1st or 2nd D{}
argument to const B&
.