派生类使用受保护的等号运算符会导致默认删除。

3
假设我们有以下实现:
class A {
    protected:
    bool operator==(const A&) const = default;
};

class B : public A {
    public:
    bool operator==(const B& b) const {
        return A::operator==(b);
    };
};

int main() {
    B x, y;
    x == y;
}

这在 gcc 12.1clang 14.0 中可行。我还假设这是 B::operator==(const B& b) 的默认行为,因为标准规定如下:

一个类可以将 operator== 定义为默认值,返回值为 bool。这将生成每个基类和成员子对象的相等比较,按照它们的声明顺序。如果发现成员或基类在声明顺序中出现不相等,则测试将短路。如果这两个对象的基类和成员的值相等,则它们相等。

所以我们只需要用 =default 替换上面的语句就可以了,对吗?其实并不完全是这样的...
class A {
    protected:
    bool operator==(const A&) const = default;
};

class B : public A {
    public:
    bool operator==(const B& b) const = default;
};

int main() {
    B x, y;
    // Fails on both clang and gcc
    // error: use of deleted function 'constexpr bool B::operator==(const B&) const'
    //     x == y;
    x == y;
}

如果运算符的默认行为不合规,则默认行为是隐式删除该函数。因此,这里的默认行为有些不规范。
我唯一的假设是我们正在尝试在A的上下文中调用A::operator==(可能通过std::static_cast(*this) == b),这是非法的,因为该函数是受保护的而不是公共的。在上面的实现中,我们从B的上下文中调用了A::operator==,这是合法的。但我没有看到标准文件中明确指定这种特定行为的地方。
附录:这是上述“=default”实现的完整编译器转储:
<source>:8:10: note: 'constexpr bool B::operator==(const B&) const' is implicitly deleted because the default definition would be ill-formed:
    8 |     bool operator==(const B& b) const = default;
      |          ^~~~~~~~
<source>:8:10: error: 'bool A::operator==(const A&) const' is protected within this context
<source>:3:10: note: declared protected here
    3 |     bool operator==(const A&) const = default;

我预计有人会问 "为什么你要这样做?"。禁止基类执行 == 操作,但允许派生类自愿执行 == 操作的想法很常见,但使用自由函数 operator==(const B& lhs, const B& rhs) 的论点是有效的。这是目前的解决方法。

1个回答

3
默认实现不会尝试调用基类比较的 A::operator==(b) 方法。相反,它会执行像 static_cast<const A&>(*this) == static_cast<const A&>(b) 的操作,并像往常一样对其进行重载决议。这在 [class.eq]/2[class.eq]/3 中有描述。还可以参考 [class.compare.default]/3[class.compare.default]/6 了解相关定义。
上述表达式将解析为static_cast<const A&>(*this).operator==(static_cast<const A&>(b))。然而,重载解析的上下文是类B,因此[class.protected]中的特殊protected规则适用,禁止左侧出现类型不匹配和访问发生的类的情况。

谢谢提供参考。我理解标准中的这个短语是“通过比较x和y的子对象扩展列表中的相应元素xi和yi”,特别是“子对象”需要进行静态向上转换,我的理解正确吗? - OneRaynyDay
@OneRaynyDay 是的,这是“扩展子对象列表”定义中的一部分,可以在 https://timsong-cpp.github.io/cppwp/n4868/class.compare.default#6.sentence-3 中找到。 - user17732522
谢谢你!很高兴知道我的假设是正确的 :) - OneRaynyDay

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