I've got the following sample code:
#include <assert.h>
struct Base
{
bool operator==(const Base& rhs) const
{
return this->equalTo(rhs);
}
virtual bool equalTo(const Base& rhs) const = 0;
};
inline bool operator!=(const Base& lhs, const Base& rhs)
{
return !(lhs == rhs);
}
struct A : public Base
{
int value = 0;
bool operator==(const A& rhs) const
{
return (value == rhs.value);
}
virtual bool equalTo(const Base& rhs) const
{
auto a = dynamic_cast<const A*>(&rhs);
return (a != nullptr) ? *this == *a : false;
}
};
class A_1 : public A
{
virtual bool equalTo(const Base& rhs) const
{
auto a_1 = dynamic_cast<const A_1*>(&rhs);
return (a_1 != nullptr) ? *this == *a_1 : false;
}
};
int main()
{
A_1 a_1;
a_1.value = 1;
// Make sure different types aren't equal
A a;
a.value = 1;
assert(a_1 != a);
}
When I compile with C++17, everything is fine (no assert, as desired). Building the same code with C++20 causes the assert to fire.
How can I get this existing code to work when compiling with C++20? If I crank up the warnings, with C++20, I get 'operator !=': unreferenced inline function has been removed; I suspect this is all somehow related to operator<=>().
Is this really a known/desired breaking change from C++17 to C++20?
In C++17, the line
assert(a_1 != a);
Invokes operator!=(const Base&, const Base&), because of course, it's the only candidate. That then invokes a_1->equalTo(a), which tries to downcast a to an A_1, which in your logic gives you false. So a_1 != a evaluates as true.
In C++20, we now additionally consider rewritten candidates in terms of ==. So now we have three candidates:
Base::operator==(const Base&) const (rewritten)A::operator==(const A&) const (rewritten)bool operator!=(const Base&, const Base&)Of these, (2) is the best candidate, since it's a better match for the two parameters. So a_1 != a evaluates as !((const A&)a_1 == a), which gives you a different answer.
However, your operators are fundamentally broken. Even in C++17, we had the scenario that a_1 != a evaluates as true while a != a_1 evaluates as false. This is basically the inherent problem of trying to do dynamic equality like this: this->equalTo(rhs) and rhs.equalTo(*this) might actually do different things. So this issue is less of a C++20 comparisons issue and more of a fundamental design issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With