Fedor
Fedor

Reputation: 21307

(x != x) for default equality operator==

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?

Upvotes: 2

Views: 196

Answers (1)

duck
duck

Reputation: 2448

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).

Upvotes: 1

Related Questions