依赖查找“(又名ADL或”Koenig Lookup“)?

什么是依赖于参数的查找的一些很好的解释? 许多人也称之为Koenig Lookup。

最好我想知道:

  • 为什么这是件好事?
  • 为什么这是一件坏事?
  • 它是如何工作的?
  • (注意:这是为了进入Stack Overflow的C ++ FAQ。)


    Koenig Lookup在C ++中也被称为参数依赖查找 ,大多数标准C ++编译器都支持它。

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

    当函数调用(5.2.2)中的postfix-expression是非限定id时,可能会搜索在通常的非限定查找(3.4.1)期间未考虑的其他名称空间,并且在这些名称空间中可能会搜索名称空间范围的好友函数声明11.3)可能会被发现。 这些对搜索的修改取决于参数的类型(以及模板参数,模板参数的名称空间)。

    简单来说,Nicolai Josuttis说:

    如果函数的名称空间中定义了一个或多个参数类型,则不必为函数限定名称空间。

    一个简单的代码示例:

    namespace MyNamespace
    {
        class MyClass {};
        void doSomething(MyClass);
    }
    
    MyNamespace::MyClass obj; // global object
    
    
    int main()
    {
        doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
    }
    

    在上面的例子中,既没有using-declaration也没有using-directive但编译器仍然通过应用Koenig算法正确地将非限定名称doSomething()作为名称空间MyNamespace声明的函数进行MyNamespace

    它是如何工作的?
    该算法告诉编译器不要只看本地范围,而是看看包含参数类型的名称空间。 因此,在上面的代码中,编译器发现作为函数doSomething()的参数的obj对象属于命名空间MyNamespace 。 因此,它会查看该名称空间以查找doSomething()的声明。

    Koenig Lookup什么优势?
    正如上面简单的代码示例所示,Koenig算法为程序员提供了方便和易用性。 如果没有Koenig算法,编程人员会花费一定的开销,反复指定完全限定名称,或者改为使用大量使用声明。

    为什么批评Koenig Algorithm
    过分依赖Koenig算法可能会导致语义问题,有时会让程序员无法防范。

    考虑一下std :: swap的例子,它是一个交换两个值的标准库算法。 使用Koenig算法时,使用这种算法时必须小心谨慎,因为:

    std::swap(obj1,obj2);    
    

    可能不会显示与以下相同的行为:

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

    使用ADL时,调用哪个版本的swap函数将取决于传递给它的参数的名称空间。
    如果存在一个名称空间A并且如果A::obj1A::obj2A::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 Lookup
    因为它是由前AT&T和贝尔实验室研究员兼程序员Andrew Koenig设计的


    良好的阅读:

    草药萨特的名字查找GotW
    标准C ++ 03/11 [basic.lookup.argdep]:3.4.2参数依赖名称查找。


    1 Koenig算法的定义与Josuttis的书“ The C ++ Standard Library:A Tutorial and Reference”中的定义一致


    在Koenig Lookup中,如果一个函数在没有指定名称空间的情况下被调用,那么函数的名称也会在名称空间中搜索,其中定义了参数的类型。 这就是为什么它也被称为参数 - 依赖名称查找,简而言之简单ADL。

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

    std::cout << "Hello World!" << "n";
    

    否则,在缺乏Koenig Lookup的情况下,我们必须写下:

    std::operator<<(std::operator<<(std::cout, "Hello World!"), "n");
    

    这实在太多了,代码看起来真的很难看!

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


    也许最好从为什么开始,然后才去了解。

    引入名称空间时,想法是在名称空间中定义所有内容,以便单独的库不会相互干扰。 但是,这给运营商带来了问题。 在下面的代码中查找示例:

    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
      ???
    }
    

    当然,你可以写成N::operator++(x) ,但是这样会打败运算符重载的整个点。 因此,必须找到一种解决方案,它允许编译器查找operator++(X&)尽管它不在范围内。 另一方面,它仍然不应该在另一个不相关的命名空间中定义另一个operator++ ,这可能会导致调用不明确(在这个简单的例子中,您不会含糊不清,但在更复杂的示例中,您可能会)。 该解决方案是参数相关查找(ADL),因为查找取决于参数(更确切地说,参数的类型),所以这种方式被称为。 由于该方案是由Andrew R. Koenig发明的,因此它也经常被称为Koenig查找。

    诀窍在于,对于函数调用,除了普通名称查找(在使用点查找范围内的名称)之外,还会在给函数的任何参数类型的范围内进行第二次查找。 因此,在上面的例子中,如果您在main中编写x++ ,它不仅在全局范围内查找operator++ ,而且在xN::X的类型被定义的范围内(即在namespace N查找operator++ 。 在那里它找到了一个匹配的operator++ ,因此x++正常工作。 但是,另一个名称空间中定义的operator++ N2 )将不会被找到。 由于ADL不限于名称空间,因此您也可以在main()使用f(x)而不是N::f(x) main()

    链接地址: http://www.djcxy.com/p/29817.html

    上一篇: Dependent Lookup" (aka ADL, or "Koenig Lookup")?

    下一篇: Namespaces and the Pre