[英]What is "Argument-Dependent Lookup" (aka ADL, or "Koenig Lookup")?
關於什么是參數依賴查找有什么好的解釋? 許多人也將其稱為 Koenig Lookup。
最好我想知道:
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 查找,在使用此算法時必須謹慎,因為:
可能不會顯示與以下相同的行為:
在 GotW 上查找 Herb Sutter 的姓名<\/a><\/li>
使用 ADL,調用哪個版本的
swap<\/code>函數將取決於傳遞給它的參數的名稱空間。
如果存在命名空間
A<\/code> ,並且存在
A::obj1<\/code> 、
A::obj2<\/code>和
A::swap()<\/code> ,則第二個示例將導致調用
A::swap()<\/code> ,這可能不是用戶想要的。
瑣事:<\/h2>
為什么稱為“Koenig 查找”?<\/h3>
進一步閱讀:<\/h3>
標准 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.