實參依賴查詢
實參依賴查詢(ADL),也稱為 Koenig 查詢[1],是查詢函式呼叫表示式中不限定函式名稱的規則集,包括對過載運算子的隱式函式呼叫。這些函式名稱除了通常不限定名稱查詢所考慮的作用域和名稱空間外,還會在其實參的名稱空間中查詢。
實參依賴查詢使得在不同名稱空間中定義的運算子成為可能。示例
#include <iostream> int main() { std::cout << "Test\n"; // There is no operator<< in global namespace, but ADL // examines std namespace because the left argument is in // std and finds std::operator<<(std::ostream&, const char*) operator<<(std::cout, "Test\n"); // Same, using function call notation // However, std::cout << endl; // Error: “endl” is not declared in this namespace. // This is not a function call to endl(), so ADL does not apply endl(std::cout); // OK: this is a function call: ADL examines std namespace // because the argument of endl is in std, and finds std::endl (endl)(std::cout); // Error: “endl” is not declared in this namespace. // The sub-expression (endl) is not an unqualified-id }
目錄 |
[編輯] 詳情
首先,如果通常的不限定查詢產生的查詢集包含以下任何一項,則不考慮實參依賴查詢:
否則,對於函式呼叫表示式中的每個實參,都會檢查其實參型別以確定它將新增到查詢中的關聯名稱空間和類集。
T
的指標或指向 T
陣列的指標的實參,檢查型別 T
,並將其關聯的類和名稱空間集新增到集合中。X
的成員函式 F
的指標的實參,檢查函式引數型別、函式返回型別和類 X
,並將其關聯的類和名稱空間集新增到集合中。X
的資料成員 T
的指標的實參,同時檢查成員型別和型別 X
,並將其關聯的類和名稱空間集新增到集合中。- 此外,如果過載集由模板識別符號命名,則檢查其所有型別模板實參和模板模板實參(但非型別模板實參除外),並將其關聯的類和名稱空間集新增到集合中。
如果關聯的類和名稱空間集中的任何名稱空間是內聯名稱空間,則其封閉名稱空間也新增到集合中。 如果關聯的類和名稱空間集中的任何名稱空間直接包含內聯名稱空間,則該內聯名稱空間新增到集合中。 |
(C++11 起) |
在確定關聯的類和名稱空間集之後,為了進一步的 ADL 處理,該集合中的類中找到的所有宣告都被丟棄,除了下面第 2 點所述的名稱空間作用域友元函式和函式模板。
普通不限定查詢找到的宣告集與 ADL 產生的關聯集所有元素中找到的宣告集合並,並遵循以下特殊規則:
[編輯] 注意
由於實參依賴查詢,與類在同一名稱空間中定義的非成員函式和非成員運算子被視為該類的公共介面的一部分(如果透過 ADL 找到它們)[2]。
ADL 是在泛型程式碼中交換兩個物件的既定慣用語背後的原因:using std::swap; swap(obj1, obj2);因為直接呼叫std::swap(obj1, obj2)不會考慮可能在obj1或obj2型別所在名稱空間中定義的使用者定義swap()
函式,而僅呼叫不限定的swap(obj1, obj2)在沒有提供使用者定義過載的情況下什麼也不會呼叫。特別是,std::iter_swap和所有其他標準庫演算法在處理可交換(Swappable)型別時都使用這種方法。
名稱查詢規則使得在全域性或使用者定義名稱空間中宣告對std
名稱空間中的型別進行操作的運算子不切實際,例如,為std::vector或std::pair定義自定義的operator>>或operator+(除非 vector/pair 的元素型別是使用者定義型別,這將它們的名稱空間新增到 ADL 中)。此類運算子不會從模板例項化(如標準庫演算法)中查詢。有關詳細資訊,請參閱依賴名稱。
ADL 可以找到完全在類或類模板中定義的友元函式(通常是過載運算子),即使它從未在名稱空間級別宣告過。
template<typename T> struct number { number(int); friend number gcd(number x, number y) { return 0; }; // Definition within // a class template }; // Unless a matching declaration is provided gcd is // an invisible (except through ADL) member of this namespace void g() { number<double> a(3), b(4); a = gcd(a, b); // Finds gcd because number<double> is an associated class, // making gcd visible in its namespace (global scope) // b = gcd(3, 4); // Error; gcd is not visible }
儘管即使普通查詢找不到任何內容,函式呼叫也可以透過 ADL 解析,但使用顯式指定模板實參對函式模板的函式呼叫要求普通查詢能夠找到模板的宣告(否則,遇到未知名稱後跟小於號是語法錯誤)。 namespace N1 { struct S {}; template<int X> void f(S); } namespace N2 { template<class T> void f(T t); } void g(N1::S s) { f<3>(s); // Syntax error until C++20 (unqualified lookup finds no f) N1::f<3>(s); // OK, qualified lookup finds the template 'f' N2::f<3>(s); // Error: N2::f does not take a non-type parameter // N1::f is not looked up because ADL only works // with unqualified names using N2::f; f<3>(s); // OK: Unqualified lookup now finds N2::f // then ADL kicks in because this name is unqualified // and finds N1::f } |
(C++20 前) |
在以下上下文中,會發生僅 ADL 查詢(即,僅在關聯名稱空間中查詢):
|
(C++11 起) |
- 從模板例項化點進行的依賴名稱查詢。
|
(C++17 起) |
[編輯] 示例
本節不完整 原因:更多示例 |
來自http://www.gotw.ca/gotw/030.htm 的示例
namespace A { struct X; struct Y; void f(int); void g(X); } namespace B { void f(int i) { f(i); // Calls B::f (endless recursion) } void g(A::X x) { g(x); // Error: ambiguous between B::g (ordinary lookup) // and A::g (argument-dependent lookup) } void h(A::Y y) { h(y); // Calls B::h (endless recursion): ADL examines the A namespace // but finds no A::h, so only B::h from ordinary lookup is used } }
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 33 | C++98 | 關聯的名稱空間或類未指定 如果用於查詢的實參是 一組過載函式或函式模板的地址 |
已指定 |
CWG 90 | C++98 | 巢狀非聯合類的關聯類 不包括其封閉類,但巢狀 聯合與其封閉類相關聯 |
非聯合也關聯 |
CWG 239 | C++98 | 在普通不限定查詢中找到的塊作用域函式宣告 並沒有阻止 ADL 發生 |
除 using 宣告外, 不考慮 ADL |
CWG 997 | C++98 | 在確定函式模板的關聯類和名稱空間時, 依賴引數型別和返回型別被排除在考慮範圍之外 已包含 |
已包含 |
CWG 1690 | C++98 C++11 |
ADL 無法找到返回的 lambda (C++11) 或本地類型別物件 (C++98) |
可以找到 |
CWG 1691 | C++11 | ADL 對於不透明列舉宣告有令人驚訝的行為 | 已修復 |
CWG 1692 | C++98 | 雙重巢狀類沒有關聯的名稱空間 (它們的封閉類不是任何名稱空間的成員) |
關聯名稱空間 擴充套件到最內層 封閉名稱空間 |
CWG 2857 | C++98 | 不完整類型別的關聯類 包括其基類 |
不包括 |
[編輯] 另請參閱
[編輯] 外部連結
|