Fedor
Fedor

Reputation: 21291

Can friend comparison operator be defined for a local class?

Since C++20 the compiler can generate default comparison operators for a class, including as friend non-member function, see (2) in cppreference.com.

I came across the code working in MSVC that does it for a local class inside a function:

void foo() {
    struct A;
    bool operator ==(const A&, const A&);
    struct A { 
        friend bool operator ==(const A&, const A&) = default;
    };
}

Unfortunately, it does not work in Clang or GCC, which complains:

error: cannot define friend function 'operator==' in a local class definition

Online demo: https://godbolt.org/z/Ts1fer1d1

There is a way to make the code accepted by GCC:

void foo() {
    struct A;
    bool operator ==(const A&, const A&);
    struct A { 
        friend bool operator ==(const A&, const A&);
    };
    bool operator ==(const A&, const A&) = default;
}

which now only prints some vague warning:

warning: declaration of 'bool operator==(const foo()::A&, const foo()::A&)' has 'extern' and is initialized

but the other two compilers does not like it, online demo: https://godbolt.org/z/he1zjj46G

As long as the compilers diverge, which one is correct in both examples above?

Upvotes: 3

Views: 113

Answers (1)

Jan Schultke
Jan Schultke

Reputation: 39668

The standard is very clear on this:

A function may be defined in a friend declaration of a class if and only if the class is a non-local class and the function name is unqualified.

- [class.friend] p6

Your first code sample is defining a friend in a local class, so it violates this paragraph.

The second example defines a function at block scope, which is also clearly ill-formed:

[...] A function shall be defined only in namespace or class scope. [...]

- [dcl.fct.def.general] p2

The fact that GCC compiles it is a compiler bug, and possibly related to the fact that GCC supports local functions as a compiler extension. The warning has 'extern' and is initialized it gives is nonsensical, and would normally occur in scenarios such as:

// <source>:1:12: warning: 'x' initialized and declared 'extern'
//     1 | extern int x = 0;
//       |            ^
extern int x = 0;

Note: I've filed a bug report for this problem: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111079

Upvotes: 3

Related Questions