Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redeclaration of explicitly defaulted comparison operator makes it undefined

In the following program, struct A has default friend equality comparison operator, which is redeclared again to get the pointer of the function (&operator==):

struct A {
    friend constexpr bool operator ==(const A &, const A &) noexcept = default;
};

static_assert( A{} == A{} ); //if this line is removed the program fails
constexpr bool operator ==(const A &, const A &) noexcept;
static_assert( (&operator==)( A{}, A{} ) );

All major compilers (GCC, Clang, MSVC) are fine with the program. Online demo: https://gcc.godbolt.org/z/dhcd8esKn

However if the line with static_assert( A{} == A{} ); is removed, then the same compilers start rejecting the program with the errors:

error: 'constexpr bool operator==(const A&, const A&)' used before its definition

note: undefined function 'operator==' cannot be used in a constant

note: failure was caused by call of undefined function or one not declared 'constexpr'

Could you please explain why above program is valid only in presence of static_assert( A{} == A{} ); before operator== redeclaration?

like image 256
Fedor Avatar asked Sep 01 '25 20:09

Fedor


1 Answers

This seems to be a bug in the implementations tested. EDG accepts the program.

The defaulted operator== should not be considered undefined because attempting to call it in a constant expression should cause its definition to be generated. See [dcl.fct.def.default]/5

[...] A non-user-provided defaulted function (i.e., implicitly declared or explicitly defaulted in the class) that is not defined as deleted is implicitly defined when it is odr-used ([basic.def.odr]) or needed for constant evaluation ([expr.const]).

"Needed for constant evaluation" is defined in [expr.const]/21

An expression or conversion is potentially constant evaluated if it is:

  • a manifestly constant-evaluated expression,
  • a potentially-evaluated expression,
  • an immediate subexpression of a braced-init-list,
  • an expression of the form & cast-expression that occurs within a templated entity, or
  • a potentially-evaluated subexpression ([intro.execution]) of one of the above.

A function or variable is needed for constant evaluation if it is:

  • a constexpr function that is named by an expression that is potentially constant evaluated, or
  • a potentially-constant variable named by a potentially constant evaluated expression.

The operand of the static assertion, (&operator==)( A{}, A{} ), is a manifestly constant-evaluated expression. The id-expression operator== within it is a potentially-evaluated subexpression, so it is potentially constant evaluated, and it names the operator== declared in this example, which is a constexpr function, so that function is needed for constant evaluation.

Note that the friend declaration and the out-of-class redeclaration declare the same function. So, although name lookup for the id-expression operator== finds only the out-of-class redeclaration in this case, that id-expression denotes the function to which that redeclaration binds the name operator==. So this expression that names the function is sufficient to make the function "needed for constant evaluation" even though the declaration found by the lookup is not the defining declaration.

like image 79
Brian Bi Avatar answered Sep 04 '25 08:09

Brian Bi