0

I'm trying to understand ADL based lookup. The following snippet results in a compilation error due to ambiguous overloads (error below). Shouldn't the version found by ADL given preference and selected instead?

template <typename It, typename F>
void for_each(It begin, It end, F f) {}

int main() {
    std::vector<int> vec;
    for_each(vec.begin(), vec.end(), [](int &x){});  
}
source>: In function 'int main()':
<source>:70:13: error: call of overloaded 'for_each(std::vector<int>::iterator, std::vector<int>::iterator, main()::<lambda(int&)>)' is ambiguous
   70 |     for_each(vec.begin(), vec.end(), [](int &x){});
      |     ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:64:6: note: candidate: 'void for_each(It, It, F) [with It = __gnu_cxx::__normal_iterator<int*, std::vector<int> >; F = main()::<lambda(int&)>]'
   64 | void for_each(It begin, It end, F f) {
      |      ^~~~~~~~
In file included from /opt/compiler-explorer/gcc-trunk-20230422/include/c++/14.0.0/algorithm:61,
                 from <source>:3:
/opt/compiler-explorer/gcc-trunk-20230422/include/c++/14.0.0/bits/stl_algo.h:3827:5: note: candidate: '_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = __gnu_cxx::__normal_iterator<int*, vector<int> >; _Funct = main()::<lambda(int&)>]'
 3827 |     for_each(_InputIterator __first, _InputIterator __last, _Function __f)
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
user3882729
  • 1,339
  • 8
  • 11
  • Do you have `using namespace std;` in your code? [It's a bad habit](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) which tend to lead to problems such as these. Please try to create a [mre] to show us, including all of the source (like `#include` and `using ...`) – Some programmer dude Apr 23 '23 at 16:14
  • The program [works here](https://godbolt.org/z/qKK4cY65r). The problem is that you have a `using namespace std;` in your code somewhere. See [this demo](https://godbolt.org/z/h9K4zexb8) which shows that if you use `using namespace std;` you can reproduce the error. – Jason Apr 23 '23 at 16:18
  • Every person having "using namespace std" must well know all thousands of identifiers and then know that `for_each` still exists in the STL – dalfaB Apr 23 '23 at 16:20
  • 2
    There is no need to a using directive to trigger this error ([proof](https://wandbox.org/permlink/nL2GLNhDPnkGvvUl)). Any would be experts should know that before closing with such a way off target. – StoryTeller - Unslander Monica Apr 23 '23 at 16:29
  • 1
    There is no ```using namespace std```, see the link https://godbolt.org/z/Ev8xd7v45 – user3882729 Apr 23 '23 at 17:36

1 Answers1

3

Shouldn't the version found by ADL given preference and selected instead?

No, that's not the function of ADL. The purpose in examining related namespaces is to find candidates that would not otherwise be found (but are arguably a natural part of the API for the types in the namespace).

Those candidates are not preferred, but instead just go into the overload resolution process along with any candidates found by regular lookup rules, and here we can have conflicts. Since both overloads of for_each we compare are generated from templates, viable, unconstrained, and don't rank higher than each other in partial ordering of function templates, the call is ambiguous.

This may seem counter-intuitive when looking at such a simple example, but halting compilation and letting programmers resolve conflicts is really the only safe option. C++ is incredibly complex, between the various ways a name can be brought into scope, and the fact multiple related namespaces can be examined during ADL, it'd be bad if the language chose another overload silently when the code around a call changed in some invisible way.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458