What is Argument-Dependent Lookup (aka ADL, or Koenig Lookup)?
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*.
Argument-dependent lookup in C++
f(new A<void*>());
Indeed works because of Argument dependent lookup/Koenig lookup(ADL)
Koenig Lookup states:
You don’t have to qualify the namespace(scope) for functions if one or more argument types are defined in the namespace of the function.
Consider a simplistic example not using templates and it should help you understand ADL at work better:
#include <iostream>
struct A
{
friend void f(A x)
{
std::cout << "A\n";
}
};
int main()
{
f(A());
return 0;
}
Output:
A
When you use f(A<int>())
, it mandates that f()
requires an argument of the type int
, but your structure does not provide any conversion from A
to int
and hence the error.
If you provide the appropriate conversion then it will work as well. Something like:
operator int(){return 1;}
Koenig lookup for arguments
Focusing on the non-duplicate part, why not foo::baz(bar());
? The simple reason is that in C++, the parse tree is always processed bottom to top. Name lookup of a function name depends on the function arguments, not the other way around, just like overload resolution depends on the argument types and not the return type.
ADL name lookup problem, is using std::swap; swap(a,b) related to function overloading or inner scope function hide outer scope function?
1.
is true. For ADL,
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.
std::swap
is found by usual unqualified name lookup, and Foo::swap
is found by ADL, they're both in the overload set. Foo::swap
is a non-template function which is perferred to std::swap
which is a template in overload resolution.
F1 is determined to be a better function than F2 if implicit
conversions for all arguments of F1 are not worse than the implicit
conversions for all arguments of F2, and...
4) or, if not that, F1 is a non-template function while F2 is a
template specialization
And about the quote from the linked answer,
if for some reason both
A::swap(A::MyClass&, A::MyClass&)
andstd::swap(A::MyClass&, A::MyClass&)
are defined,
There's no std::swap(A::MyClass&, A::MyClass&)
in fact, it's just an assumption, if there's a non-template std::swap(A::MyClass&, A::MyClass&)
then the invocation would be ambiguous ...
g++ std::visit leaking into global namespace?
The argument visitor
is an std::function
, which is in the namespace std
, so argument-dependent lookup finds visit
in the namespace std
as well.
If you always want the visit
in the global namespace, say so with ::visit
.
What's preventing Boost.Format form using my stream operator overload for optional int?
boost::format("%s") % i
invokes a call to operator<<
. Name lookup rule is followed during compiling to find a operator<<
.
For boost::format("%s") % t
, both struct SomeType
and std::ostream& operator<<(std::ostream& os, const SomeType& t)
is defined in global namespace, by using ADL, operator<<
is found.
For (boost::format("%s") % i)
, std::optional
is defined in namespace std
, but corresponding operator<<
is defined in global namespace. By using ADL, boost won't be able to find it. And
non-ADL lookup examines function declarations with external linkage that are visible from the template definition context,
so the compiler isn't able to find the operator<<
which you defined.
A workaround: wrap std::optional inside your own ReferenceWrapper, then define the inserter for your wrapper in the same namespace where ReferenceWrapper is defined.
Related Topics
How to Remove "Noise" from Gcc/Clang Assembly Output
Is Segmentation Fault Actual Undefined Behavior When We Refer to a Non-Static Data-Member
How to Add Reflection to a C++ Application
Returning Multiple Values from a C++ Function
What Is a "Cache-Friendly" Code
Placement of the Asterisk in Pointer Declarations
Why Aren't My Include Guards Preventing Recursive Inclusion and Multiple Symbol Definitions
How to Execute a Functor or a Lambda in a Given Thread in Qt, Gcd-Style
Fastest Way to Check If a File Exists Using Standard C++/C++11,14,17/C
How to Print Bytes as Hexadecimal
Countdown Until Matching Digits
Why Should I Use a Pointer Rather Than the Object Itself
Running My C++ Code Gives Me a Blank Console