Does Argument-Dependent Lookup go before normal scope lookup? - c++

This is the code in question that appears in §13.3 of "C++ Primer", 5ed:
void swap(Foo &lhs, Foo &rhs)
{
using std::swap;
swap(lhs.h, rhs.h); // uses the HasPtr version of swap
// swap other members of type Foo
}
The book mentions the phenomenon of the class-specific swap not being hidden by the using declaration, and refers reader to §18.2.3:
I read that section and realized that this may be related to Argument-Dependent Lookup (ADL). The following is the excerption:
But I still have some vagueness in the understanding. My question is: does the ADL go before the normal scope lookup, or after the normal scope lookup? My current understanding is that ADL goes before the normal scope lookup, because otherwise it should be the std::swap that is used. I need confirmation if you think I am right, or please point out what mistake I've made if you think I am wrong. Thank you.

ADL doesn't go before, it's not preferred specially; the names found by ADL will be considered in addition to the names found by the usual name lookup.
These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup.
That means all the named found by ADL and usual name lookup will be considered in overload resolution; then the best match will be selected.
In order to compile a function call, the compiler must first perform name lookup, which, for functions, may involve argument-dependent lookup, and for function templates may be followed by template argument deduction. If these steps produce more than one candidate function, then overload resolution is performed to select the function that will actually be called.

Related

C++ scope resolution operator before funcions [duplicate]

What are some good explanations on what argument dependent lookup is? Many people also call it Koenig Lookup as well.
Preferably I'd like to know:
Why is it a good thing?
Why is it a bad thing?
How does it work?
Koenig Lookup, or Argument Dependent Lookup, describes how unqualified names are looked up by the compiler in C++.
The C++11 standard § 3.4.2/1 states:
When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function declarations (11.3) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template
argument).
In simpler terms Nicolai Josuttis states1:
You don’t have to qualify the namespace for functions if one or more argument types are defined in the namespace of the function.
A simple code example:
namespace MyNamespace
{
class MyClass {};
void doSomething(MyClass) {}
}
MyNamespace::MyClass obj; // global object
int main()
{
doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}
In the above example there is neither a using-declaration nor a using-directive but still the compiler correctly identifies the unqualified name doSomething() as the function declared in namespace MyNamespace by applying Koenig lookup.
How does it work?
The algorithm tells the compiler to not just look at local scope, but also the namespaces that contain the argument's type. Thus, in the above code, the compiler finds that the object obj, which is the argument of the function doSomething(), belongs to the namespace MyNamespace. So, it looks at that namespace to locate the declaration of doSomething().
What is the advantage of Koenig lookup?
As the simple code example above demonstrates, Koenig lookup provides convenience and ease of usage to the programmer. Without Koenig lookup there would be an overhead on the programmer, to repeatedly specify the fully qualified names, or instead, use numerous using-declarations.
Why the criticism of Koenig lookup?
Over-reliance on Koenig lookup can lead to semantic problems, and catch the programmer off guard sometimes.
Consider the example of std::swap, which is a standard library algorithm to swap two values. With the Koenig lookup one would have to be cautious while using this algorithm because:
std::swap(obj1,obj2);
may not show the same behavior as:
using std::swap;
swap(obj1, obj2);
With ADL, which version of swap function gets called would depend on the namespace of the arguments passed to it.
If there exists a namespace A, and if A::obj1, A::obj2, and A::swap() exist, then the second example will result in a call to A::swap(), which might not be what the user wanted.
Further, if for some reason both A::swap(A::MyClass&, A::MyClass&) and std::swap(A::MyClass&, A::MyClass&) are defined, then the first example will call std::swap(A::MyClass&, A::MyClass&) but the second will not compile because swap(obj1, obj2) would be ambiguous.
Trivia:
Why is it called “Koenig lookup”?
Because it was devised by former AT&T and Bell Labs researcher and programmer, Andrew Koenig.
Further reading:
Herb Sutter's Name Lookup on GotW
Standard C++03/11 [basic.lookup.argdep]: 3.4.2 Argument-dependent name lookup.
**1** The definition of Koenig lookup is as defined in Josuttis' book, *The C++ Standard Library: A Tutorial and Reference*.
In Koenig Lookup, if a function is called without specifying its namespace, then the name of a function is also searched in namespace(s) in which the type of the argument(s) is defined. That is why it is also known as Argument-Dependent name Lookup, in short simply ADL.
It is because of Koenig Lookup, we can write this:
std::cout << "Hello World!" << "\n";
Otherwise, we would have to write:
std::operator<<(std::operator<<(std::cout, "Hello World!"), "\n");
which really is too much typing and the code looks really ugly!
In other words, in the absence of Koenig Lookup, even a Hello World program looks complicated.
Maybe it is best to start with the why, and only then go to the how.
When namespaces were introduced, the idea was to have everything defined in namespaces, so that separate libraries don't interfere with each other. However that introduced a problem with operators. Look for example at the following code:
namespace N
{
class X {};
void f(X);
X& operator++(X&);
}
int main()
{
// define an object of type X
N::X x;
// apply f to it
N::f(x);
// apply operator++ to it
???
}
Of course you could have written N::operator++(x), but that would have defeated the whole point of operator overloading. Therefore a solution had to be found which allowed the compiler to find operator++(X&) despite the fact that it was not in scope. On the other hand, it still should not find another operator++ defined in another, unrelated namespace which might make the call ambiguous (in this simple example, you wouldn't get ambiguity, but in more complex examples, you might). The solution was Argument Dependent Lookup (ADL), called that way since the lookup depends on the argument (more exactly, on the argument's type). Since the scheme was invented by Andrew R. Koenig, it is also often called Koenig lookup.
The trick is that for function calls, in addition to normal name lookup (which finds names in scope at the point of use), there is done a second lookup in the scopes of the types of any arguments given to the function. So in the above example, if you write x++ in main, it looks for operator++ not only in global scope, but additionally in the scope where the type of x, N::X, was defined, i.e. in namespace N. And there it finds a matching operator++, and therefore x++ just works. Another operator++ defined in another namespace, say N2, will not be found, however. Since ADL is not restricted to namespaces, you also can use f(x) instead of N::f(x) in main().
Not everything about it is good, in my opinion. People, including compiler vendors, have been insulting it because of its sometimes unfortunate behavior.
ADL is responsible for a major overhaul of the for-range loop in C++11. To understand why ADL can sometimes have unintended effects, consider that not only the namespaces where the arguments are defined are considered, but also the arguments of template arguments of the arguments, of parameter types of function types / pointee types of pointer types of those arguments, and so on and forth.
An example using boost
std::vector<boost::shared_ptr<int>> v;
auto x = begin(v);
This resulted in an ambiguity if the user uses the boost.range library, because both std::begin is found (by ADL using std::vector) and boost::begin is found (by ADL using boost::shared_ptr).

Why is the scope operator for std namespace not required in this case? [duplicate]

What are some good explanations on what argument dependent lookup is? Many people also call it Koenig Lookup as well.
Preferably I'd like to know:
Why is it a good thing?
Why is it a bad thing?
How does it work?
Koenig Lookup, or Argument Dependent Lookup, describes how unqualified names are looked up by the compiler in C++.
The C++11 standard § 3.4.2/1 states:
When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function declarations (11.3) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template
argument).
In simpler terms Nicolai Josuttis states1:
You don’t have to qualify the namespace for functions if one or more argument types are defined in the namespace of the function.
A simple code example:
namespace MyNamespace
{
class MyClass {};
void doSomething(MyClass) {}
}
MyNamespace::MyClass obj; // global object
int main()
{
doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}
In the above example there is neither a using-declaration nor a using-directive but still the compiler correctly identifies the unqualified name doSomething() as the function declared in namespace MyNamespace by applying Koenig lookup.
How does it work?
The algorithm tells the compiler to not just look at local scope, but also the namespaces that contain the argument's type. Thus, in the above code, the compiler finds that the object obj, which is the argument of the function doSomething(), belongs to the namespace MyNamespace. So, it looks at that namespace to locate the declaration of doSomething().
What is the advantage of Koenig lookup?
As the simple code example above demonstrates, Koenig lookup provides convenience and ease of usage to the programmer. Without Koenig lookup there would be an overhead on the programmer, to repeatedly specify the fully qualified names, or instead, use numerous using-declarations.
Why the criticism of Koenig lookup?
Over-reliance on Koenig lookup can lead to semantic problems, and catch the programmer off guard sometimes.
Consider the example of std::swap, which is a standard library algorithm to swap two values. With the Koenig lookup one would have to be cautious while using this algorithm because:
std::swap(obj1,obj2);
may not show the same behavior as:
using std::swap;
swap(obj1, obj2);
With ADL, which version of swap function gets called would depend on the namespace of the arguments passed to it.
If there exists a namespace A, and if A::obj1, A::obj2, and A::swap() exist, then the second example will result in a call to A::swap(), which might not be what the user wanted.
Further, if for some reason both A::swap(A::MyClass&, A::MyClass&) and std::swap(A::MyClass&, A::MyClass&) are defined, then the first example will call std::swap(A::MyClass&, A::MyClass&) but the second will not compile because swap(obj1, obj2) would be ambiguous.
Trivia:
Why is it called “Koenig lookup”?
Because it was devised by former AT&T and Bell Labs researcher and programmer, Andrew Koenig.
Further reading:
Herb Sutter's Name Lookup on GotW
Standard C++03/11 [basic.lookup.argdep]: 3.4.2 Argument-dependent name lookup.
**1** The definition of Koenig lookup is as defined in Josuttis' book, *The C++ Standard Library: A Tutorial and Reference*.
In Koenig Lookup, if a function is called without specifying its namespace, then the name of a function is also searched in namespace(s) in which the type of the argument(s) is defined. That is why it is also known as Argument-Dependent name Lookup, in short simply ADL.
It is because of Koenig Lookup, we can write this:
std::cout << "Hello World!" << "\n";
Otherwise, we would have to write:
std::operator<<(std::operator<<(std::cout, "Hello World!"), "\n");
which really is too much typing and the code looks really ugly!
In other words, in the absence of Koenig Lookup, even a Hello World program looks complicated.
Maybe it is best to start with the why, and only then go to the how.
When namespaces were introduced, the idea was to have everything defined in namespaces, so that separate libraries don't interfere with each other. However that introduced a problem with operators. Look for example at the following code:
namespace N
{
class X {};
void f(X);
X& operator++(X&);
}
int main()
{
// define an object of type X
N::X x;
// apply f to it
N::f(x);
// apply operator++ to it
???
}
Of course you could have written N::operator++(x), but that would have defeated the whole point of operator overloading. Therefore a solution had to be found which allowed the compiler to find operator++(X&) despite the fact that it was not in scope. On the other hand, it still should not find another operator++ defined in another, unrelated namespace which might make the call ambiguous (in this simple example, you wouldn't get ambiguity, but in more complex examples, you might). The solution was Argument Dependent Lookup (ADL), called that way since the lookup depends on the argument (more exactly, on the argument's type). Since the scheme was invented by Andrew R. Koenig, it is also often called Koenig lookup.
The trick is that for function calls, in addition to normal name lookup (which finds names in scope at the point of use), there is done a second lookup in the scopes of the types of any arguments given to the function. So in the above example, if you write x++ in main, it looks for operator++ not only in global scope, but additionally in the scope where the type of x, N::X, was defined, i.e. in namespace N. And there it finds a matching operator++, and therefore x++ just works. Another operator++ defined in another namespace, say N2, will not be found, however. Since ADL is not restricted to namespaces, you also can use f(x) instead of N::f(x) in main().
Not everything about it is good, in my opinion. People, including compiler vendors, have been insulting it because of its sometimes unfortunate behavior.
ADL is responsible for a major overhaul of the for-range loop in C++11. To understand why ADL can sometimes have unintended effects, consider that not only the namespaces where the arguments are defined are considered, but also the arguments of template arguments of the arguments, of parameter types of function types / pointee types of pointer types of those arguments, and so on and forth.
An example using boost
std::vector<boost::shared_ptr<int>> v;
auto x = begin(v);
This resulted in an ambiguity if the user uses the boost.range library, because both std::begin is found (by ADL using std::vector) and boost::begin is found (by ADL using boost::shared_ptr).

Why don't I need std:: in front of move with strings? [duplicate]

What are some good explanations on what argument dependent lookup is? Many people also call it Koenig Lookup as well.
Preferably I'd like to know:
Why is it a good thing?
Why is it a bad thing?
How does it work?
Koenig Lookup, or Argument Dependent Lookup, describes how unqualified names are looked up by the compiler in C++.
The C++11 standard § 3.4.2/1 states:
When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function declarations (11.3) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template
argument).
In simpler terms Nicolai Josuttis states1:
You don’t have to qualify the namespace for functions if one or more argument types are defined in the namespace of the function.
A simple code example:
namespace MyNamespace
{
class MyClass {};
void doSomething(MyClass) {}
}
MyNamespace::MyClass obj; // global object
int main()
{
doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}
In the above example there is neither a using-declaration nor a using-directive but still the compiler correctly identifies the unqualified name doSomething() as the function declared in namespace MyNamespace by applying Koenig lookup.
How does it work?
The algorithm tells the compiler to not just look at local scope, but also the namespaces that contain the argument's type. Thus, in the above code, the compiler finds that the object obj, which is the argument of the function doSomething(), belongs to the namespace MyNamespace. So, it looks at that namespace to locate the declaration of doSomething().
What is the advantage of Koenig lookup?
As the simple code example above demonstrates, Koenig lookup provides convenience and ease of usage to the programmer. Without Koenig lookup there would be an overhead on the programmer, to repeatedly specify the fully qualified names, or instead, use numerous using-declarations.
Why the criticism of Koenig lookup?
Over-reliance on Koenig lookup can lead to semantic problems, and catch the programmer off guard sometimes.
Consider the example of std::swap, which is a standard library algorithm to swap two values. With the Koenig lookup one would have to be cautious while using this algorithm because:
std::swap(obj1,obj2);
may not show the same behavior as:
using std::swap;
swap(obj1, obj2);
With ADL, which version of swap function gets called would depend on the namespace of the arguments passed to it.
If there exists a namespace A, and if A::obj1, A::obj2, and A::swap() exist, then the second example will result in a call to A::swap(), which might not be what the user wanted.
Further, if for some reason both A::swap(A::MyClass&, A::MyClass&) and std::swap(A::MyClass&, A::MyClass&) are defined, then the first example will call std::swap(A::MyClass&, A::MyClass&) but the second will not compile because swap(obj1, obj2) would be ambiguous.
Trivia:
Why is it called “Koenig lookup”?
Because it was devised by former AT&T and Bell Labs researcher and programmer, Andrew Koenig.
Further reading:
Herb Sutter's Name Lookup on GotW
Standard C++03/11 [basic.lookup.argdep]: 3.4.2 Argument-dependent name lookup.
**1** The definition of Koenig lookup is as defined in Josuttis' book, *The C++ Standard Library: A Tutorial and Reference*.
In Koenig Lookup, if a function is called without specifying its namespace, then the name of a function is also searched in namespace(s) in which the type of the argument(s) is defined. That is why it is also known as Argument-Dependent name Lookup, in short simply ADL.
It is because of Koenig Lookup, we can write this:
std::cout << "Hello World!" << "\n";
Otherwise, we would have to write:
std::operator<<(std::operator<<(std::cout, "Hello World!"), "\n");
which really is too much typing and the code looks really ugly!
In other words, in the absence of Koenig Lookup, even a Hello World program looks complicated.
Maybe it is best to start with the why, and only then go to the how.
When namespaces were introduced, the idea was to have everything defined in namespaces, so that separate libraries don't interfere with each other. However that introduced a problem with operators. Look for example at the following code:
namespace N
{
class X {};
void f(X);
X& operator++(X&);
}
int main()
{
// define an object of type X
N::X x;
// apply f to it
N::f(x);
// apply operator++ to it
???
}
Of course you could have written N::operator++(x), but that would have defeated the whole point of operator overloading. Therefore a solution had to be found which allowed the compiler to find operator++(X&) despite the fact that it was not in scope. On the other hand, it still should not find another operator++ defined in another, unrelated namespace which might make the call ambiguous (in this simple example, you wouldn't get ambiguity, but in more complex examples, you might). The solution was Argument Dependent Lookup (ADL), called that way since the lookup depends on the argument (more exactly, on the argument's type). Since the scheme was invented by Andrew R. Koenig, it is also often called Koenig lookup.
The trick is that for function calls, in addition to normal name lookup (which finds names in scope at the point of use), there is done a second lookup in the scopes of the types of any arguments given to the function. So in the above example, if you write x++ in main, it looks for operator++ not only in global scope, but additionally in the scope where the type of x, N::X, was defined, i.e. in namespace N. And there it finds a matching operator++, and therefore x++ just works. Another operator++ defined in another namespace, say N2, will not be found, however. Since ADL is not restricted to namespaces, you also can use f(x) instead of N::f(x) in main().
Not everything about it is good, in my opinion. People, including compiler vendors, have been insulting it because of its sometimes unfortunate behavior.
ADL is responsible for a major overhaul of the for-range loop in C++11. To understand why ADL can sometimes have unintended effects, consider that not only the namespaces where the arguments are defined are considered, but also the arguments of template arguments of the arguments, of parameter types of function types / pointee types of pointer types of those arguments, and so on and forth.
An example using boost
std::vector<boost::shared_ptr<int>> v;
auto x = begin(v);
This resulted in an ambiguity if the user uses the boost.range library, because both std::begin is found (by ADL using std::vector) and boost::begin is found (by ADL using boost::shared_ptr).

Usual unqualified lookup and Argument-dependent name lookup(ADL)

For unqualified name lookup, 'Usual unqualified lookup' and 'Argument-dependent name lookup'(ADL), I cannot find in standard which one happens first ?
Again as both trying to add something to the overload candidate set, the order doesn't seems to be matter though. But would still be nice to know which one happens first.
Thanks
It doesn't matter which comes first, does it? Name lookup generates a set of possible names.
3.4 Name lookup
Name lookup may associate more than one declaration with a name if it finds the name to be a function name; the declarations are said to form a set of overloaded functions.
The standard specifically says "set". As a mathematical construct, the set {1,2,3} is the same as {2,3,1} and {3,2,1}. Order doesn't matter. What does matter is when overload resolution and the application access rules occurs. Here the standard is very clear. Quoting once again from the intro paragraph to 3.4:
Overload resolution takes place after name lookup has succeeded. The access rules are considered only once name lookup and function overload resolution have succeeded.

What is "Argument-Dependent Lookup" (aka ADL, or "Koenig Lookup")?

What are some good explanations on what argument dependent lookup is? Many people also call it Koenig Lookup as well.
Preferably I'd like to know:
Why is it a good thing?
Why is it a bad thing?
How does it work?
Koenig Lookup, or Argument Dependent Lookup, describes how unqualified names are looked up by the compiler in C++.
The C++11 standard § 3.4.2/1 states:
When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function declarations (11.3) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template
argument).
In simpler terms Nicolai Josuttis states1:
You don’t have to qualify the namespace for functions if one or more argument types are defined in the namespace of the function.
A simple code example:
namespace MyNamespace
{
class MyClass {};
void doSomething(MyClass) {}
}
MyNamespace::MyClass obj; // global object
int main()
{
doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}
In the above example there is neither a using-declaration nor a using-directive but still the compiler correctly identifies the unqualified name doSomething() as the function declared in namespace MyNamespace by applying Koenig lookup.
How does it work?
The algorithm tells the compiler to not just look at local scope, but also the namespaces that contain the argument's type. Thus, in the above code, the compiler finds that the object obj, which is the argument of the function doSomething(), belongs to the namespace MyNamespace. So, it looks at that namespace to locate the declaration of doSomething().
What is the advantage of Koenig lookup?
As the simple code example above demonstrates, Koenig lookup provides convenience and ease of usage to the programmer. Without Koenig lookup there would be an overhead on the programmer, to repeatedly specify the fully qualified names, or instead, use numerous using-declarations.
Why the criticism of Koenig lookup?
Over-reliance on Koenig lookup can lead to semantic problems, and catch the programmer off guard sometimes.
Consider the example of std::swap, which is a standard library algorithm to swap two values. With the Koenig lookup one would have to be cautious while using this algorithm because:
std::swap(obj1,obj2);
may not show the same behavior as:
using std::swap;
swap(obj1, obj2);
With ADL, which version of swap function gets called would depend on the namespace of the arguments passed to it.
If there exists a namespace A, and if A::obj1, A::obj2, and A::swap() exist, then the second example will result in a call to A::swap(), which might not be what the user wanted.
Further, if for some reason both A::swap(A::MyClass&, A::MyClass&) and std::swap(A::MyClass&, A::MyClass&) are defined, then the first example will call std::swap(A::MyClass&, A::MyClass&) but the second will not compile because swap(obj1, obj2) would be ambiguous.
Trivia:
Why is it called “Koenig lookup”?
Because it was devised by former AT&T and Bell Labs researcher and programmer, Andrew Koenig.
Further reading:
Herb Sutter's Name Lookup on GotW
Standard C++03/11 [basic.lookup.argdep]: 3.4.2 Argument-dependent name lookup.
**1** The definition of Koenig lookup is as defined in Josuttis' book, *The C++ Standard Library: A Tutorial and Reference*.
In Koenig Lookup, if a function is called without specifying its namespace, then the name of a function is also searched in namespace(s) in which the type of the argument(s) is defined. That is why it is also known as Argument-Dependent name Lookup, in short simply ADL.
It is because of Koenig Lookup, we can write this:
std::cout << "Hello World!" << "\n";
Otherwise, we would have to write:
std::operator<<(std::operator<<(std::cout, "Hello World!"), "\n");
which really is too much typing and the code looks really ugly!
In other words, in the absence of Koenig Lookup, even a Hello World program looks complicated.
Maybe it is best to start with the why, and only then go to the how.
When namespaces were introduced, the idea was to have everything defined in namespaces, so that separate libraries don't interfere with each other. However that introduced a problem with operators. Look for example at the following code:
namespace N
{
class X {};
void f(X);
X& operator++(X&);
}
int main()
{
// define an object of type X
N::X x;
// apply f to it
N::f(x);
// apply operator++ to it
???
}
Of course you could have written N::operator++(x), but that would have defeated the whole point of operator overloading. Therefore a solution had to be found which allowed the compiler to find operator++(X&) despite the fact that it was not in scope. On the other hand, it still should not find another operator++ defined in another, unrelated namespace which might make the call ambiguous (in this simple example, you wouldn't get ambiguity, but in more complex examples, you might). The solution was Argument Dependent Lookup (ADL), called that way since the lookup depends on the argument (more exactly, on the argument's type). Since the scheme was invented by Andrew R. Koenig, it is also often called Koenig lookup.
The trick is that for function calls, in addition to normal name lookup (which finds names in scope at the point of use), there is done a second lookup in the scopes of the types of any arguments given to the function. So in the above example, if you write x++ in main, it looks for operator++ not only in global scope, but additionally in the scope where the type of x, N::X, was defined, i.e. in namespace N. And there it finds a matching operator++, and therefore x++ just works. Another operator++ defined in another namespace, say N2, will not be found, however. Since ADL is not restricted to namespaces, you also can use f(x) instead of N::f(x) in main().
Not everything about it is good, in my opinion. People, including compiler vendors, have been insulting it because of its sometimes unfortunate behavior.
ADL is responsible for a major overhaul of the for-range loop in C++11. To understand why ADL can sometimes have unintended effects, consider that not only the namespaces where the arguments are defined are considered, but also the arguments of template arguments of the arguments, of parameter types of function types / pointee types of pointer types of those arguments, and so on and forth.
An example using boost
std::vector<boost::shared_ptr<int>> v;
auto x = begin(v);
This resulted in an ambiguity if the user uses the boost.range library, because both std::begin is found (by ADL using std::vector) and boost::begin is found (by ADL using boost::shared_ptr).