C++ 什么是“参数相关查找”(又名 ADL,或“Koenig 查找”)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8111677/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:01:05  来源:igfitidea点击:

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

c++argument-dependent-lookupname-lookupc++-faq

提问by user965369

What are some good explanations on what argument dependent lookup is? Many people also call it Koenig Lookup as well.

关于什么是参数相关查找有什么好的解释?许多人也称它为 Koenig Lookup。

Preferably I'd like to know:

最好我想知道:

  • Why is it a good thing?
  • Why is it a bad thing?
  • How does it work?
  • 为什么是好事?
  • 为什么是坏事?
  • 它是如何工作的?

回答by Alok Save

Koenig Lookup, or Argument Dependent Lookup, describes how unqualified names are looked up by the compiler in C++.

Koenig LookupArgument Dependent Lookup描述了编译器如何在 C++ 中查找非限定名称。

The C++11 standard § 3.4.2/1 states:

C++11 标准 § 3.4.2/1 指出:

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).

当函数调用 (5.2.2) 中的后缀表达式是非限定 ID 时,可以搜索在通常的非限定查找 (3.4.1) 期间未考虑的其他名称空间,并且在这些名称空间中,名称空间范围的友元函数声明( 11.3) 可能会发现不可见。这些对搜索的修改取决于参数的类型(对于模板模板参数,模板参数的命名空间)。

In simpler terms Nicolai Josuttis states1:

简而言之,Nicolai Josuttis 指出1

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 MyNamespaceby applying Koenig lookup.

在上面的示例中,既没有using-declaration 也没有-directive,using但编译器仍然通过应用Koenig 查找将非限定名称正确识别doSomething()为命名空间中声明的函数。MyNamespace

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().

该算法告诉编译器不仅要查看局部作用域,还要查看包含参数类型的命名空间。因此,在上面的代码中,编译器发现obj作为函数参数的对象doSomething()属于命名空间MyNamespace。因此,它会查看该命名空间以定位doSomething().

What is the advantage of Koenig lookup?

Koenig 查找的优势是什么?

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.

正如上面的简单代码示例所示,Koenig 查找为程序员提供了便利和易用性。如果没有 Koenig 查找,程序员会产生开销,需要重复指定完全限定的名称,或者改为使用大量 -using声明。

Why the criticism of Koenig lookup?

为什么批评 Koenig 查找?

Over-reliance on Koenig lookup can lead to semantic problems, and catch the programmer off guard sometimes.

过度依赖 Koenig 查找会导致语义问题,有时会让程序员措手不及。

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,它是交换两个值的标准库算法。对于 Koenig 查找,在使用此算法时必须谨慎,因为:

std::swap(obj1,obj2);

may not show the same behavior as:

可能不会表现出与以下相同的行为:

using std::swap;
swap(obj1, obj2);

With ADL, which version of swapfunction gets called would depend on the namespace of the arguments passed to it.

使用 ADL,swap调用哪个版本的函数将取决于传递给它的参数的命名空间。

If there exists an namespace Aand if A::obj1, A::obj2& A::swap()exist then the second example will result in a call to A::swap(), which might not be what the user wanted.

如果存在命名空间A并且如果A::obj1, A::obj2&A::swap()存在,那么第二个示例将导致对 的调用A::swap(),这可能不是用户想要的。

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.

此外,如果由于某种原因同时定义了A::swap(A::MyClass&, A::MyClass&)std::swap(A::MyClass&, A::MyClass&),则第一个示例将调用,std::swap(A::MyClass&, A::MyClass&)但第二个示例将无法编译,因为swap(obj1, obj2)会产生歧义。

Trivia:

琐事:

Why is it called “Koenig lookup”?

为什么称为“Koenig 查找”?

Because it was devised by former AT&T and Bell Labs researcher and programmer, Andrew Koenig.

因为它是由前 AT&T 和贝尔实验室研究员兼程序员Andrew Koenig 设计的

Further reading:

进一步阅读:



1The definition of Koenig lookup is as defined in Josuttis' book, The C++ Standard Library: A Tutorial and Reference.

1Koenig 查找的定义在 Josuttis 的书The C++ Standard Library: A Tutorial and Reference 中定义

回答by Nawaz

In Koenig Lookup, if a function is called without specifying its namespace, then the name of a function is alsosearched 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.

在 Koenig Lookup 中,如果在未指定名称空间的情况下调用函数,则还会在定义了参数类型的名称空间中搜索函数的名称。这就是为什么它也被称为Argument-Dependent name Lookup,简而言之就是ADL

It is because of Koenig Lookup, we can write this:

正是因为 Koenig Lookup,我们可以这样写:

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 Worldprogram looks complicated.

换句话说,在没有 Koenig Lookup 的情况下,即使是一个Hello World程序看起来也很复杂。

回答by celtschk

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.

当然,您可以编写N::operator++(x),但这会破坏运算符重载的全部意义。因此,必须找到一个解决方案,允许编译器找到,operator++(X&)尽管它不在范围内。另一方面,它仍然不应该operator++在另一个不相关的命名空间中找到另一个定义,这可能会使调用产生歧义(在这个简单的示例中,您不会产生歧义,但在更复杂的示例中,您可能会产生歧义)。解决方案是 Argument Dependent Lookup (ADL),因为查找取决于参数(更准确地说,取决于参数的类型),所以这样称呼。由于该方案是由 Andrew R. Koenig 发明的,因此它通常也被称为 Koenig 查找。

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().

诀窍在于,对于函数调用,除了正常的名称查找(在使用时在范围内查找名称)之外,还会在给定函数的任何参数的类型的范围内进行第二次查找。所以在上面的例子,如果你写x++在主,它看起来operator++不仅在全球范围内,但附加在类型范围xN::X,定义,即namespace N。在那里它找到了一个匹配的operator++,因此x++可以正常工作。另一个operator++在另一个空间中定义的,比方说N2,将不会被发现然而,。由于 ADL 不限于命名空间,您也可以使用inf(x)代替。N::f(x)main()

回答by Johannes Schaub - litb

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.

ADL 负责对 C++11 中的 for-range 循环进行大修。要理解为什么 ADL 有时会产生意想不到的影响,不仅要考虑定义参数的命名空间,还要考虑参数的模板参数、函数类型的参数类型/这些参数的指针类型的指针类型的参数,以此类推。

An example using boost

一个使用 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::beginis found (by ADL using std::vector) and boost::beginis found (by ADL using boost::shared_ptr).

如果用户使用 boost.range 库,这会导致歧义,因为两者都std::begin被找到(通过 ADL 使用std::vector)和boost::begin被找到(通过 ADL 使用boost::shared_ptr)。