[Accepted as a DR at the March, 2024 meeting.]
Consider:
struct Base {
protected:
bool operator==(const Base& other) const = default;
};
struct Child : Base {
int i;
bool operator==(const Child& other) const = default;
};
bool b = Child() == Child(); //error: deleted operator==
Per 11.10.1 [class.compare.default] paragraph 6,
Let xi be an lvalue denoting the i-th element in the expanded list of subobjects for an object x (of length n), where xi is formed by a sequence of derived-to-base conversions (12.2.4.2 [over.best.ics]), class member access expressions (7.6.1.5 [expr.ref]), and array subscript expressions (7.6.1.2 [expr.sub]) applied to x.
The derived-to-base conversion for this loses the context of access to the protected Base::operator==, violating 11.8.5 [class.protected] paragraph 1. The example is rejected by implementations, but ought to work.
For this related example, there is implementation divergence:
struct B { protected: constexpr operator int() const { return 0; } }; struct D : B { constexpr bool operator==(const D&) const = default; }; template<typename T> constexpr auto comparable(T t) -> decltype(t == t) { return t == t; } constexpr bool comparable(...) { return false; } static_assert(comparable(D{}));
Is D::operator== deleted, because its defaulted definition violates the protected access rules? Is D::operator== not deleted, but synthesis fails on use because of the proctected access rules? Is the synthesis not in the immediate context, making the expression comparable(D{}) ill-formed?