Reputation: 73
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?
Upvotes: 2
Views: 576
Reputation: 302852
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.
Upvotes: 10