I'm trying to bring a function into consideration with a using
declaration:
namespace n {
struct S {};
bool equal(S a, S b, std::vector<int> = {1,2}) { return false; }
}
int main() {
using ::n::equal;
using ::n::S;
/// ...
}
This works well as long as no std::initializer_list
is passed as the third argument:
equal(S{},S{});
equal(S{},S{},{1});
But, it breaks when a std::initializer_list
is passed as the third argument:
equal(S{},S{},std::initializer_list<int> {1, 2});
It only considers std::equal
.
I struggle to understand the reason why this is not working, and to find a solution to enable this pattern.
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.