簡體   English   中英

"什么是“依賴於參數的查找”(又名 ADL,或“Koenig 查找”)?"

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

關於什么是參數依賴查找有什么好的解釋? 許多人也將其稱為 Koenig Lookup。

最好我想知道:

  • 為什么這是一件好事?<\/li>
  • 為什么這是一件壞事?<\/li>
  • 它是如何工作的?<\/li><\/ul>"

Koenig Lookup<\/strong>或Argument Dependent Lookup<\/a><\/strong>描述了 C++ 中的編譯器如何查找非限定名稱。

C++11 標准§ 3.4.2\/1 規定:

當函數調用 (5.2.2) 中的后綴表達式是非限定 ID 時,可能會搜索在通常的非限定查找 (3.4.1) 期間未考慮的其他命名空間,並且在這些命名空間中,命名空間范圍的友元函數聲明 ( 11.3) 可能會發現其他不可見的。 這些對搜索的修改取決於參數的類型(對於模板模板參數,模板參數的命名空間)。

用更簡單的術語 Nicolai Josuttis 說1<\/sup> :

如果在函數的命名空間中定義了一個或多個參數類型,則不必限定函數的命名空間。

一個簡單的代碼示例:

在上面的示例中,既沒有using<\/code> -declaration 也沒有using<\/code> -directive,但編譯器仍然通過應用Koenig lookup<\/em>將非限定名稱doSomething()<\/code>正確識別為命名空間MyNamespace<\/code>中聲明的函數。

它是如何工作的?<\/h3>

該算法告訴編譯器不僅要查看本地范圍,還要查看包含參數類型的命名空間。 因此,在上面的代碼中,編譯器發現作為函數doSomething()<\/code>的參數的對象obj<\/code>屬於命名空間MyNamespace<\/code> 。 因此,它會查看該命名空間來定位doSomething()<\/code>的聲明。

Koenig 查找的優勢是什么?<\/h3>

正如上面的簡單代碼示例所示,Koenig 查找為程序員提供了便利和易用性。 如果沒有 Koenig 查找,程序員會產生開銷,重復指定完全限定的名稱,或者改為使用大量using<\/code> -declarations。

為什么要批評 Koenig 查找?<\/h3>

過度依賴 Koenig 查找會導致語義問題,有時會讓程序員措手不及。

考慮std::swap<\/code><\/a><\/strong>的示例,它是交換兩個值的標准庫算法。 對於 Koenig 查找,在使用此算法時必須謹慎,因為:

可能不會顯示與以下相同的行為:

使用 ADL,調用哪個版本的swap<\/code>函數將取決於傳遞給它的參數的名稱空間。

如果存在命名空間A<\/code> ,並且存在A::obj1<\/code> 、 A::obj2<\/code>和A::swap()<\/code> ,則第二個示例將導致調用A::swap()<\/code> ,這可能不是用戶想要的。

此外,如果由於某種原因同時定義了A::swap(A::MyClass&, A::MyClass&)<\/code>和std::swap(A::MyClass&, A::MyClass&)<\/code> ,那么第一個示例將調用std::swap(A::MyClass&, A::MyClass&)<\/code>但第二個不會編譯,因為swap(obj1, obj2)<\/code>會模棱兩可。

瑣事:<\/h2>

為什么稱為“Koenig 查找”?<\/h3>

因為它是由前 AT&T 和貝爾實驗室研究員兼程序員Andrew Koenig<\/a><\/strong>設計的。

進一步閱讀:<\/h3>
在 GotW 上查找 Herb Sutter 的姓名<\/a>

<\/li>
  • 標准 C++03\/11 [basic.lookup.argdep]:3.4.2 依賴於參數的名稱查找。

    <\/li><\/ul>


    ** 1<\/sup> ** Koenig 查找的定義在 Josuttis 的書中定義,*C++ 標准庫:教程和參考*。<\/sub>"
  • 在 Koenig Lookup 中,如果調用函數時未指定其命名空間,則函數名稱也會<\/em>在定義參數類型的命名空間中搜索。 這就是為什么它也被稱為Argument-Dependent name Lookup<\/a> ,簡稱ADL<\/a> 。

    這是因為 Koenig Lookup,我們可以這樣寫:

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

    也許最好從為什么開始,然后才去如何。

    引入命名空間時,想法是在命名空間中定義所有內容,以便單獨的庫不會相互干擾。 然而,這給運營商帶來了問題。 例如看下面的代碼:

    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++ ,這可能會使調用模棱兩可(在這個簡單的示例中,您不會模棱兩可,但在更復雜的示例中,您可能會感到模棱兩可)。 解決方案是 Argument Dependent Lookup (ADL),因為查找取決於參數(更准確地說,取決於參數的類型),所以這樣調用。 由於該方案是由 Andrew R. Koenig 發明的,因此通常也稱為 Koenig 查找。

    訣竅在於,對於函數調用,除了正常的名稱查找(在使用點查找范圍內的名稱)外,還會在給函數的任何參數的類型的范圍內進行第二次查找。 所以在上面的例子中,如果你在 main 中編寫x++ ,它不僅在全局范圍內查找operator++ ,而且還在定義x類型N::X的范圍內,即在namespace N中查找。 在那里它找到了一個匹配的operator++ ,因此x++就可以工作了。 但是,將找不到在另一個命名空間中定義的另一個operator++ ,比如N2 由於 ADL 不限於命名空間,因此您還可以在main()中使用f(x)而不是N::f(x) )。

    在我看來,並非一切都很好。 人們,包括編譯器供應商,一直在侮辱它,因為它有時是不幸的行為。

    ADL 負責對 C++11 中的 for-range 循環進行大修。 要理解為什么 ADL 有時會產生意想不到的效果,請考慮不僅要考慮定義參數的命名空間,還要考慮參數的模板參數、函數類型的參數類型\/這些參數的指針類型的指針類型的參數,依此類推。

    一個使用 boost 的例子

    std::vector<boost::shared_ptr<int>> v;
    auto x = begin(v);
    

    暫無
    暫無

    聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

     
    粵ICP備18138465號  © 2020-2024 STACKOOM.COM