I am attempting to create a class that wraps another template-specified type, like a Box
. I also want to add operators to enable comparing Box
('s stored value) against any type that its stored value is comparable to. A requires
clause is used to check that the comparison is valid. Here is a simple example:
template <class T> struct Box {
T val;
template <class... Args> Box(Args&&... args)
: val(std::forward<Args>(args)...) {}
template <class O> bool operator==(const O& b) const
requires requires { val == b; } { return val == b; }
};
int main() {
Box<int> a(5);
a == 5;
}
This compiles and runs fine with operator==
as a member function. However, if I rewrite the operator==
as a friend function, it falls apart:
//...
template <class O> friend bool operator==(const Box& a, const O& b)
requires requires { a.val == b; } { return a.val == b; }
//...
When compiled on GCC:
<source>: In substitution of 'template<class O> bool operator==(const Box<int>&, const O&) requires requires{operator==::a->val == operator==::b;} [with O = int]':
<source>:14:29: required by substitution of 'template<class O> bool operator==(const Box<int>&, const O&) requires requires{operator==::a->val == operator==::b;} [with O = int]'
<source>:20:7: required from here
<source>:13:33: required by the constraints of 'template<class T> template<class O> bool operator==(const Box<T>&, const O&) requires requires{operator==::a->val == operator==::b;}'
<source>:14:12: in requirements [with T = int; O = Box<int>]
<source>:14:26: error: satisfaction of atomic constraint 'requires{operator==::a->val == operator==::b;} [with T = T; O = O]' depends on itself
14 | requires requires { a.val == b; } { return a.val == b; }
| ^~~~~~~~~~~~~~~~~~~~~~~~
This appears on all the most recent versions of GCC, in both C++20 and C++23 modes. Clang gives a similar error. The same problem also occurs (albeit with a longer error message) if I use a concept instead of a requires
expression, such as std::equality_comparable_with
. The only way to fix this error is to not check the validity of the comparison expression in the requires
clause.
It seems that the compiler is considering the enclosing operator function in the overload set of the requires
constraint, causing the operator to be infinitely instantiated recursively. But I don't quite get how that is possible? I thought friend functions could only be found by ADL, and neither of the operands in the requires
expression is a Box
(in this instantiation, both are int
), which should additionally fail due to type argument mismatch, and the fact that int
doesn't have a member val
(after the first recursion). Can someone explain what is going on, and if possible, how this can be fixed?