什么是“参数相关查找” (又名 ADL,或“Koenig Lookup”)?
关于什么是参数依赖查找有哪些好的解释?许多人也将其称为 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
参数依赖查找 (ADL),有时称为 Koenig Lookup,描述了 C++ 编译器如何查找非限定名称。
C++11 标准 § 3.4.2/1 规定:
简而言之,Nicolai Josuttis 指出1:
一个简单的代码示例:
在上面的示例中,既没有
using
声明,也没有using
指令,但编译器仍然正确识别非限定名称doSomething()
作为通过应用 ADL 在命名空间MyNamespace
中声明的函数。它是如何运作的?
该算法告诉编译器不仅要查看本地范围,还要查看包含参数类型的命名空间。因此,在上面的代码中,编译器发现对象
obj
(函数doSomething()
的参数)属于命名空间MyNamespace.因此,它会查看该名称空间来定位
doSomething()
的声明。ADL有什么优势?
正如上面的简单代码示例所示,ADL 为程序员提供了便利性和易用性。如果没有 ADL,程序员就会产生开销,需要重复指定完全限定名称,或者使用大量
using
声明。为什么ADL受到批评?
过度依赖 ADL 可能会导致语义问题,有时会让程序员措手不及。
考虑
std::swap
的示例,其中是交换两个值的标准库算法。使用 ADL 时,必须谨慎使用此算法,因为:可能不会显示与以下行为相同的行为:
使用 ADL,调用哪个版本的
swap
函数将取决于传递给的参数的名称空间它。如果存在命名空间
A
,并且如果A::obj1
、A::obj2
和A::swap()
存在,那么第二个示例将导致调用A::swap()
,这可能不是用户想要的。此外,如果由于某种原因
A::swap(A::MyClass&, A::MyClass&)
和std::swap(A::MyClass&, A::MyClass& ;)
被定义,那么第一个示例将调用std::swap(A::MyClass&, A::MyClass&)
但第二个示例将无法编译,因为swap(obj1, obj2)
是不明确的。冷知识:
为什么有时称为“Koenig 查找”?
因为它是由前 AT&T 和贝尔实验室研究员和程序员 Andrew Koenig,尽管柯尼格本人对此提出异议在2012 年的一篇博文:
进一步阅读:
赫伯·萨特 (Herb Sutter) 在 GotW 上的名字查找
标准 C++03/11 [基本。 lookup.argdep]:3.4.2 参数相关名称查找。
**1** The definition of ADL is as defined in Josuttis' book, *The C++ Standard Library: A Tutorial and Reference*.
Argument Dependent Lookup (ADL), sometimes called Koenig Lookup, describes how unqualified names are looked up by the compiler in C++.
The C++11 standard § 3.4.2/1 states:
In simpler terms Nicolai Josuttis states1:
A simple code example:
In the above example there is neither a
using
-declaration nor ausing
-directive but still the compiler correctly identifies the unqualified namedoSomething()
as the function declared in namespaceMyNamespace
by applying ADL.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 functiondoSomething()
, belongs to the namespaceMyNamespace
. So, it looks at that namespace to locate the declaration ofdoSomething()
.What is the advantage of ADL?
As the simple code example above demonstrates, ADL provides convenience and ease of usage to the programmer. Without ADL 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 ADL?
Over-reliance on ADL 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 ADL one would have to be cautious while using this algorithm because:may not show the same behavior as:
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 ifA::obj1
,A::obj2
, andA::swap()
exist, then the second example will result in a call toA::swap()
, which might not be what the user wanted.Further, if for some reason both
A::swap(A::MyClass&, A::MyClass&)
andstd::swap(A::MyClass&, A::MyClass&)
are defined, then the first example will callstd::swap(A::MyClass&, A::MyClass&)
but the second will not compile becauseswap(obj1, obj2)
would be ambiguous.Trivia:
Why is it sometimes called a “Koenig lookup”?
Because it was devised by former AT&T and Bell Labs researcher and programmer, Andrew Koenig, although Koenig himself disputes this in a 2012 blog post:
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 ADL is as defined in Josuttis' book, *The C++ Standard Library: A Tutorial and Reference*.
在 Koenig Lookup 中,如果调用函数时未指定其命名空间,则也会在定义参数类型的命名空间中搜索函数名称。这就是为什么它也被称为参数依赖名称查找,简而言之就是ADL。
正是因为有了Koenig Lookup,我们才能这样写:
否则,我们就得这样写:
这实在是打字太多了,而且代码看起来真的很难看!
换句话说,在没有 Koenig Lookup 的情况下,即使是 Hello World 程序看起来也很复杂。
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:
Otherwise, we would have to write:
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.
也许最好从“为什么”开始,然后再讨论“如何”。
当引入命名空间时,我们的想法是在命名空间中定义所有内容,以便单独的库不会相互干扰。然而,这给运营商带来了一个问题。例如,请看下面的代码:
当然,您可以编写
N::operator++(x)
,但这会破坏运算符重载的全部意义。因此,必须找到一种解决方案,允许编译器找到operator++(X&)
,尽管它不在范围内。另一方面,它仍然不应该找到在另一个不相关的命名空间中定义的另一个operator++
,这可能会使调用不明确(在这个简单的示例中,您不会得到歧义,但在更复杂的示例中,你可能会)。解决方案是参数相关查找 (ADL),这样称呼是因为查找取决于参数(更准确地说,取决于参数的类型)。由于该方案是由 Andrew R. Koenig 发明的,因此通常也称为 Koenig 查找。技巧在于,对于函数调用,除了正常的名称查找(在使用时在范围内查找名称)之外,还会在给定函数的任何参数的类型范围中进行第二次查找。因此,在上面的示例中,如果您在 main 中编写
x++
,它不仅会在全局范围内查找operator++
,还会在x 类型所在的范围内查找
,operator++
N::X
,被定义,即在命名空间N
中。在那里它找到了一个匹配的operator++
,因此x++
就可以工作了。但是,将找不到在另一个命名空间(例如N2
)中定义的另一个operator++
。由于 ADL 不限于运算符,您还可以在main()
中使用f(x)
代替N::f(x)
。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:
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 findoperator++(X&)
despite the fact that it was not in scope. On the other hand, it still should not find anotheroperator++
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 foroperator++
not only in global scope, but additionally in the scope where the type ofx
,N::X
, was defined, i.e. innamespace N
. And there it finds a matchingoperator++
, and thereforex++
just works. Anotheroperator++
defined in another namespace, sayN2
, will not be found, however. Since ADL is not restricted to operators, you also can usef(x)
instead ofN::f(x)
inmain()
.在我看来,并非所有事情都是好的。人们,包括编译器供应商,一直在侮辱它,因为它有时会出现不幸的行为。
ADL 负责对 C++11 中的 for-range 循环进行重大修改。要理解为什么 ADL 有时会产生意想不到的效果,请考虑不仅考虑定义参数的命名空间,还考虑参数的模板参数、函数类型的参数类型/这些参数的指针类型的参数类型,等等。
使用 boost 的示例
如果用户使用 boost.range 库,这会导致歧义,因为找到了两个
std::begin
(通过 ADL 使用std::vector
)并且找到了boost::begin
(通过 ADL 使用boost::shared_ptr
)。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
This resulted in an ambiguity if the user uses the boost.range library, because both
std::begin
is found (by ADL usingstd::vector
) andboost::begin
is found (by ADL usingboost::shared_ptr
).