当父类本身没有定义宇宙飞船操作符时,如何覆写它?

3
以下代码在编译器中受到不同的处理:
#include <compare>

struct A;

struct I {
    virtual std::strong_ordering operator <=>(const A&) const { 
        return std::strong_ordering::equal; 
    }
};

struct A : I {
    virtual std::strong_ordering operator <=>(const A&) const = default;
};

GNU 编译器和 Microsoft Visual C++ 都支持此语法,但 Clang 不支持并会返回错误:

warning: explicitly defaulted three-way comparison operator is implicitly deleted [-Wdefaulted-function-deleted]
    virtual std::strong_ordering operator <=>(const A&) const = default;
defaulted 'operator<=>' is implicitly deleted because there is no viable three-way comparison function for base class 'I'
error: deleted function 'operator<=>' cannot override a non-deleted function
    virtual std::strong_ordering operator <=>(const A&) const = default;

演示:https://gcc.godbolt.org/z/WGrGTe89z

看起来只有 Clang 正确。因为 I::operator <=>(const I&) const 没有被定义,所以 A::operator <=>(const A&) const 必须被隐式删除,而一个被删除的方法无法覆盖 I 中未被删除的方法。其他编译器接受这段代码也是正确的吗?


6
虚拟比较运算符是一个充满陷阱的领域。 - Caleth
1个回答

2
一旦您编写类似于 A a; a < a; 的代码,其他编译器也会拒绝该代码,这使我认为 Clang 拒绝它过早了。在 [class.compare.default] 中,标准规定:

当类 C 的比较运算符函数在其第一次声明上被默认并且未定义为已删除时,在需要进行常量求值或 odr-used 时将隐式定义。 在比较运算符函数的默认定义中进行的名称查找是从等同于其函数体的上下文中执行的。 出现在类中作为默认的比较运算符的定义应为该函数的第一次声明。

由于您的示例中没有解析为 A::operator<=>(const A&) 的表达式,因此不需要定义它,即使该函数最终总是被删除,也不应该被拒绝。


谢谢。实际上,其他编译器也会隐式删除 A::operator <=>(const A&) const,但这应该是一个编译错误,因为具有这种签名的方法是从基类继承而来的虚函数,不能在子类中被删除。这导致其他编译器在链接时出现错误(GCC)或生成无效代码(MSVC),演示:https://gcc.godbolt.org/z/cfsa3ncq6 - Fedor
@Fedor:三种不同的结果!真是一团糟。那个演示代码相当不好,我可以看出Clang想要防止你挂起自己。但是,我认为(i <=> a)不是odr-using A::operator<=>(const A&),因此标准可能甚至不需要对这种欺诈行为发出警告。 - sigma
我报告了GCC的错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103689 - Fedor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接