I'm going to number these calls:
equal(S{},S{}); // #1
equal(S{},S{},{1}); // #2
equal(S{},S{},std::initializer_list<int>{1, 2}); // #3
So in #1 and #2, the only viable candidate is n::equal, so it gets called and everything works.
But in #3, std::equal suddenly becomes a candidate - it's found by argument-dependent lookup (ADL) because of the explicit std::initializer_list argument. And there is an overload of std::equal that looks like this:
template<class InputIterator1, class InputIterator2>
constexpr bool equal(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2);
Note that while the template parameters are named InputIterator1 and InputIterator2, and those types really should be input iterators, the algorithm itself is not constrained, so it is considered to be a viable candidate†.
And between n::equal and std::equal, the latter is a better match - all the arguments match exactly, whereas for n::equal an explicit conversion is required from std::initializer_list<int> to std::vector<int>. As such, std::equal is selected - which then won't compile because none of thse types are iterators.
The simplest solutions are to just not use ADL, or to not pass an explicit std::initializer_list<int> and just manually pass a std::vector<int>.
Alternatively, you could add an extra overload of n::equal to handle that case:
bool equal(S a, S b, std::vector<int> = {1,2});
bool equal(S a, S b, std::initializer_list<int> xs) {
return equal(a, b, std::vector<int>(xs));
}
†In constrast, std::ranges::equal is both constrained and cannot be found by ADL.
{1}will be translated to astd::initializer_list<int>by the compiler. Which will then use it to create the temporarystd::vector<int>object that is passed to the function. There's almost no use-case to create astd::initializer_list<T>object explicitly.std::equal. That's because of ADL (Argument-Dependent Lookup), which happens because one of the arguments (the initializer list object) is part of thestdnamespace.std::initializer_list, argument dependent lookup is choosingstd::equalas yourequal. The simplest solution for this is to calln::equal, rather than asking the compiler to choose whichequalfunction to use.<source>:11:5: note: in instantiation of function template specialization 'std::equal<n::S, std::initializer_list<int>>' requested hereyou are notified that notn::equalis used.struct MyEnum { static constexpr int a = 1; static constexpr int b = 2; static constexpr auto ab = { a, b }; };