(reduced from a user of boost/operators.hpp) template <typename> class A; template <typename T> bool operator==(const A<T>&, int) { return false; } template <typename> class A { friend bool operator==(int y, const A& x) { return x == y; } }; int main(){ A<short> q; q==3; 3==q; } $ g++ -std=c++2a a.c -Wall && ./a.out a.c: In instantiation of 'bool operator==(int, const A<short int>&)': a.c:10:6: required from here a.c:5:56: warning: in C++20 this comparison calls the current function recursively with reversed arguments 5 | friend bool operator==(int y, const A& x) { return x == y; } | ~~^~~~ zsh: segmentation fault ./a.out If I make both operators friends, or move both outside, gcc is happy, but in this mixed case, it doesn't seem to want to use the first operator== and prefers the rewritten second operator==. Of course removing the second operator== completely also works. Clang is fine with this version of the code. I have trouble parsing the standard wording, but IIRC one of the principles when adding <=> was that explicitly written functions should have priority over new, invented ones. Bug 93807 is the closest I could find.
It looks like clang-10+ also generates an infinite loop on this code. Does the standard really give priority to some implicit function over a user-defined one that is an exact match?
Ah, maybe the friend function is not quite a template, so the generated swapped function is not a template either, and thus it has priority over a template if both are exact matches? This is going to break a number of users of boost/operators.hpp, and possibly other mixins using a similar technique.
It seems that this is as currently specified in C++20, but that some people are going to try and change the rules to avoid breaking code like this.
(In reply to Marc Glisse from comment #3) > It seems that this is as currently specified in C++20, but that some people > are going to try and change the rules to avoid breaking code like this. Do you have reference to the discussion on the subject?
(In reply to Marc Glisse from comment #1) > It looks like clang-10+ also generates an infinite loop on this code. clang 12+ now does not produce an infinite loop. GCC trunk still does though.