c++language-lawyerc++20constexprc++23

Explicitly constexpr default comparisons


After a regression in GCC 13.3, I had to add constexpr to defaulted spaceship operators in my program, and observed some discrepancies in how compilers treat some code in C++20 mode. These can be shown on equality comparison operator for the sake of code brevity below.

Example #1:

struct A {
    operator int() const;
};

struct B : A {
    constexpr bool operator==(const B &) const = default;
};

which is ok for Clang and MSVC, but GCC with -pedantic-errors option complains here:

error: call to non-'constexpr' function 'A::operator int() const' [-Winvalid-constexpr]

Example #2:

struct A {
    bool operator==(const A &) const;
};

struct B : A {
    constexpr bool operator==(const B &) const = default;
};

which ok only for MSVC, while Clang with -pedantic-errors option starts rejecting it as well with the

error: defaulted definition of equality comparison operator that is declared constexpr but invokes a non-constexpr comparison function is a C++23 extension [-Werror,-Wc++23-default-comp-relaxed-constexpr]

Are both code examples valid only in C++23 and not valid in C++20?


Solution

  • This is P2448R2, Relaxing some constexpr restrictions. Particularly, for OPs example, it removes the following restriction:

    (removed) [dcl.constexpr]/6 For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, an evaluated subexpression of the initialization full-expression of some constant-initialized object ([basic.start.static]), the program is ill-formed, no diagnostic required.

    And also removes the concept of constexpr-compatible for defaulted comparison functions:

    (removed) [class.compare.default]/4 A defaulted comparison function is constexpr-compatible if it satisfies the requirements for a constexpr function ([dcl.constexpr]) and no overload resolution performed when determining whether to delete the function results in a usable candidate that is a non-constexpr function.

    (modified/added) [dcl.fct.def.default]/3 An explicitly-defaulted function that is not defined as deleted may be declared constexpr or consteval only if it is constexpr-compatible ([special], [class.compare.default]). A function explicitly defaulted on its first declaration is implicitly inline ([dcl.inline]), and is implicitly constexpr ([dcl.constexpr]) if it is constexpr-compatible satisfies the requirements for a constexpr function.