Description
UFCS has a huge problem with fallback semantics as it was discussed in the wiki.
There you say:
Here are some of the major alternative semantics we could choose for UFCS, in rough order of likelihood of unwanted silent changes in behavior. Note that this list is not exhaustive!
<...>
C) Overload and select the best match. Adding a new member or nonmember function could still silently change behavior (so this does not eliminate potential accidental surprises), but if it does it's because you're getting a better match which is at least something.
<...>
Right now, I think B and C are likely the best choices, <...>
The situation with C can be greatly improved if we could also make our decision using the constraints.
main: () = {
using std::ranges::_;
// 1
m: std::map<char, char> = ();
map_it: _ is std::input_iterator = m.find('o'); // ok, uses the member function
// 2
v: std::vector<char> = ();
vec_it: _ is std::input_iterator = v.find('o'); // ok, uses `std::ranges::find`
// 3
s: std::string = ();
str_it: _ is std::input_iterator = s.find('o'); // currently an error, but should it be that way?
}
I think we should also consider the constraints when resolving the overload using UFCS to pick the best match.
With these improvements, (3) should compile to cpp1 like so:
if constexpr(requires { { s.find('o') } -> std::input_iterator; }) {
return s.find('o');
} else if constexpr(requires { { find(s, 'o') -> std::input_iterator }; }) {
return find(s, 'o');
}