Argument-dependent lookup -- when is it done, what is searched, and how can you force (or prevent) it?

StackOverflow https://stackoverflow.com/questions/14168890

  •  13-01-2022
  •  | 
  •  

Question

I'm having trouble understanding the rules behind argument-dependent (Koenig) lookup.

Consider the code below:

#include <iostream>

using namespace std;

namespace adl
{
    struct Test { };
    void foo1(Test const &) { cout << "ADL used (foo1)" << endl; }
    void foo2(Test const &) { cout << "ADL used (foo2)" << endl; }
    void foo3(Test const &) { cout << "ADL used (foo3)" << endl; }
}

struct foo1
{
    foo1() { }

    template<class T>
    foo1(T const &) { cout << "ADL not used (foo1)" << endl; }

    template<class T>
    void operator()(T const &) const { cout << "ADL not used (foo3)" << endl; }
};

template<class T> void foo2(T const &)
{ cout << "ADL not used (foo2)" << endl; }

int main()
{
    adl::Test t;
    foo1 foo3;
    (foo1(t));
    (foo2(t));
    (foo3(t));
}

Its output is:

ADL not used (foo1)
ADL used (foo2)
ADL not used (foo3)

I expected all of them to use ADL, but I was surprised that only some of them did.

What are the (potentially gory, I know) details behind the rules of ADL?
I understand the concept well enough, but the details are what I'm having trouble with.

Which scopes are searched, when are they searched, and when are they not searched?

Is it at all possible to tell whether ADL is used without having to look through all the #include'd files before the given line of code? I expected functors and functions to behave the same way in terms of [not] masking ADL, but apparently they don't.

Is there any way to force ADL in cases where it is not done automatically (such as the above) and you don't know the class's namespace (e.g. in a template)?

Was it helpful?

Solution

Your problem isn't really with argument dependent lookup. First of all, argument dependent lookup only possibly enters the picture when doing unqualified looking up of functions. When calling foo1(t) foo1 is a type and its templated constructor is called. Similarly, foo3(t) is a qualified lookup because foo3 is an object and the function call operator is looked up in the object's class foo1. The only place where argument lookup enters the picture is calling foo2(t) where lookup finds to candidates:

  1. ::foo2<adl::Test>(adl::Test const&)
  2. ::adl::foo2(adl::Test const&)

These two functions are handed off to overload resolution and since both functions are equally good matches the non-template function wins.

Your question are actually three questions:

  1. The gory details of name lookup are too broad and, thus, this question is a request for an essay to be written which I ignore.
  2. You second question expands to three more questions, only one seems relevant:
    1. Which scopes are searched? When looking up an unqualified function name inside a function definition the rules depend on whether any of the names is a dependent name. If there is no such name (i.e., in non-template code or in template code where the names can be determined in phase one), the name is looked up enclosing namespaces and in namespaces associated with its argument. Otherwise, the name is only looked up in the associated namespaces.
  3. Can argument dependent lookup be forced? It is always done for unqualified function lookups if there is, at least, one argument but names found otherwise may be better matches. Of course, you need to call an unqualified function otherwise it won't be done.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top