Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does virtual `operator <=>` with default implementation make one more virtual method in C++20?

If one makes virtual C++20 three-way comparison operator <=> with the default implementation, then the compiler silently makes one more virtual operator==, as can be shown by overriding it in a child class:

struct A { 
    virtual std::strong_ordering operator <=>(const A &) const = default; 
};
struct B : A {
    virtual bool operator==(const A&) const noexcept override;
};

All three major compilers behaves that way, demo: https://gcc.godbolt.org/z/nvaf94M51

Is it mandated by the standard or just a common implementation practice?

As a side observation, in C++20 we also have an ability to make consteval immediate virtual functions, and if the example is extended:

#include <compare>

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

struct B : A {
    virtual consteval bool operator==(const A&) const noexcept override { return false; }
};

int main() {
    static constexpr B b;
    static constexpr const A & a = b;
    static_assert( (a <=> a) == 0 );
    static_assert( a != a );
    return (a <=> a) == 0; 
}

then it introduces certain difficulties to the modern compilers: https://gcc.godbolt.org/z/Gsz39Kr5b

Clang crashes, and GCC produces a program, which fails during execution. Are these just some compiler bugs, or the program is not well formed?

like image 492
Fedor Avatar asked Sep 06 '21 06:09

Fedor


1 Answers

Is it mandated by the standard or just a common implementation practice?

It is mandated. [class.compare.default]/5:

If the member-specification does not explicitly declare any member or friend named operator==, an == operator function is declared implicitly for each three-way comparison operator function defined as defaulted in the member-specification, with the same access and function-definition and in the same class scope as the respective three-way comparison operator function, except that the return type is replaced with bool and the declarator-id is replaced with operator==.

[Note 2: Such an implicitly-declared == operator for a class X is defined as defaulted in the definition of X and has the same parameter-declaration-clause and trailing requires-clause as the respective three-way comparison operator. It is declared with friend, virtual, constexpr, or consteval if the three-way comparison operator function is so declared. ... — end note]

[Example 1:

template<typename T> struct X {
 friend constexpr std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default;
 // implicitly declares: friend constexpr bool operator==(X, X) requires (sizeof(T) != 1) = default;

 [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default;
 // implicitly declares: [[nodiscard]] virtual bool operator==(const X&) const = default;
};

— end example]

like image 121
Language Lawyer Avatar answered Oct 17 '22 22:10

Language Lawyer