This program
struct A {
int i = 0;
constexpr operator int() const { return i; }
constexpr operator int&() { return ++i; }
};
struct B {
A a;
bool operator==(const B &) const = default;
};
constexpr bool f() {
B x;
return x == x;
}
static_assert( f() ); // fails in GCC
fails static assertion in GCC compiler, meaning that x != x
is true
during constant evaluation, where operator==
is generated by the compiler. Online demo: https://gcc.godbolt.org/z/4xjTnM54M
If one modifies struct B
such that it inherits from A
instead of having it as a field:
struct B : A {
bool operator==(const B &) const = default;
};
constexpr bool f() {
B x;
return x == x;
}
static_assert( f() ); // fails in Clang
then the program succeeds in GCC but fails in Clang. Online demo: https://gcc.godbolt.org/z/5hYob3K66
Are both programs well formed and f()
must be evaluated to true
?
This is just a bug in GCC/Clang.
A defaulted equality operator is specified to perform a memberwise equality comparison of its operands' base class subobjects, followed by non-static data members, in declaration order ([class.eq]/3).
The comparison is performed as-if by a == b
, where a
and b
are lvalues denoting the corresponding subobjects of the operands. [class.compare.default]/5 explicitly says that such lvalues are
formed by a sequence of derived-to-base conversions, class member access expressions, and array subscript expressions applied to
x
where x
is the respective parameter of the defaulted equality operator, which is necessarily const
-qualified.
So, following the normal rules of the language, a
and b
should also inherit this const
-qualification (except when referring to mutable
data members), but for some reason this does not happen in GCC for non-static data members, whereas Clang coincidentally exhibits the same buggy behavior with base class subobjects.
In the OP example, this bug leads to A::operator int&()
being selected over A::operator int() const
to perform the A
->int
conversion for the built-in operator==(int, int)
.