C++17运算符==()和运算符!=()代码在C++20中失败

2022-05-17 00:00:00 operator-overloading c++ c++20 c++17

我有以下示例代码:

#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);
}

当我用C++17编译时,一切都很好(如所需,没有断言)。使用C++20生成相同的代码会导致触发Assert。

在使用C++20编译时,如何使这些现有代码工作?如果我用C++20放大警告,我会得到'operator !=': unreferenced inline function has been removed;我怀疑这一切都与operator<=>()有某种关系。

这真的是从C++17到C++20的已知/所需的重大更改吗?


解决方案

在C++17中,行

assert(a_1 != a);
调用operator!=(const Base&, const Base&),因为它当然是唯一的候选者。然后调用a_1->equalTo(a),它试图将a向下转换为A_1,这在您的逻辑中会给出FALSE。因此a_1 != a评估为true


在C++20中,我们现在额外考虑根据==重写的候选对象。因此,现在我们有三位候选人:

  1. Base::operator==(const Base&) const(重写)
  2. A::operator==(const A&) const(重写)
  3. bool operator!=(const Base&, const Base&)
在这些参数中,(2)是最好的候选者,因为它更适合这两个参数。因此a_1 != a计算为!((const A&)a_1 == a),这为您提供了不同的答案。


然而,您的操作符从根本上说是失败的。即使在C++17中,我们也有这样的场景:a_1 != a计算为true,而a != a_1计算为false。这基本上是试图像这样做动态相等的固有问题:this->equalTo(rhs)rhs.equalTo(*this)实际上可能会做不同的事情。因此,这个问题与其说是C++20比较问题,不如说是一个基本的设计问题。

相关文章