参数依赖查找(ADL,Argument-dependent lookup)
參數(shù)依賴(lài)查找(Argument-dependent lookup),又稱(chēng) ADL 或 Koenig 查找,是一組于函數(shù)調(diào)用表達(dá)式查找非限定函數(shù)名的規(guī)則,包含對(duì)重載運(yùn)算符的隱式函數(shù)調(diào)用。在通常非限定名稱(chēng)查找所考慮的作用域和命名空間之外,還在其參數(shù)的命名空間中查找這些函數(shù)。
?
參數(shù)依賴(lài)查找使使用定義于不同命名空間的運(yùn)算符可行。例如:
1 #include <iostream> 2 int main() 3 { 4 std::cout << "Test\n"; // 全局命名空間無(wú) operator<< ,但 ADL 檢驗(yàn) std 命名空間, 5 // 因?yàn)樽髤?shù)在 std 命名空間中 6 // 并找到 std::operator<<(std::ostream&, const char*) 7 operator<<(std::cout, "Test\n"); // 同上,用函數(shù)調(diào)用記法 8 9 // 然而, 10 std::cout << endl; // 錯(cuò)誤: 'endl' 不聲明于此命名空間。 11 // 此非對(duì) endl() 的函數(shù)調(diào)用,故不應(yīng)用 ADL 12 13 endl(std::cout); // OK :這是函數(shù)調(diào)用: ADL 檢驗(yàn) std 命名空間, 14 // 因?yàn)?endl 的參數(shù)在 std ,并找到 std::endl 15 16 (endl)(std::cout); // 錯(cuò)誤: 'endl' 不聲明于此命名空間。 17 // 子表達(dá)式 (endl) 不是函數(shù)調(diào)用表達(dá)式 18 }細(xì)節(jié)
首先,若通常非限定查找所生成的集合含有下列任何內(nèi)容,則不考慮參數(shù)依賴(lài)查找:
1)?類(lèi)成員聲明 2)?塊作用域的函數(shù)聲明(之非?using 聲明者) 3)?任何非函數(shù)或函數(shù)模板之聲明(例如,函數(shù)對(duì)象或另一變量,其名與正在查找的函數(shù)名沖突)否則,對(duì)于每個(gè)函數(shù)調(diào)用表達(dá)式中的參數(shù),檢驗(yàn)其類(lèi)型,以確定它將添加到查找的命名空間與類(lèi)的關(guān)聯(lián)集。
1)?對(duì)于基礎(chǔ)類(lèi)型參數(shù),命名空間與類(lèi)的關(guān)聯(lián)集為空集 2)?對(duì)于類(lèi)類(lèi)型(含聯(lián)合體)參數(shù),集合由以下組成 a)?類(lèi)自身 b)?其所有直接與間接基類(lèi) c)?若類(lèi)是另一類(lèi)的成員,則為該外圍類(lèi) d)?添加到集合的類(lèi)的最內(nèi)層外圍命名空間 3)?對(duì)于是類(lèi)模板特化的參數(shù)類(lèi)型,在上述規(guī)則外,還檢驗(yàn)下列規(guī)則,并添加其關(guān)聯(lián)類(lèi)與命名空間到集合 a)?為類(lèi)型模板形參提供的所有模板實(shí)參類(lèi)型(跳過(guò)非類(lèi)型模板形參并跳過(guò)模板模板形參) b)?任何模板模板實(shí)參是其中成員的命名空間 c)?任何模板模板實(shí)參是其中成員的類(lèi)(若它們恰好是類(lèi)成員模板) 4)?對(duì)于任何枚舉類(lèi)型參數(shù),添加枚舉定義于其中的命名空間到集合。若枚舉類(lèi)型是類(lèi)成員,則添加該類(lèi)到集合。 5)?對(duì)于指向 T 類(lèi)型指針或指向 T 數(shù)組的指針,檢驗(yàn)類(lèi)型 T 并添加其類(lèi)與命名空間的關(guān)聯(lián)集到集合。 6)?對(duì)于函數(shù)類(lèi)型參數(shù),檢驗(yàn)函數(shù)參數(shù)類(lèi)型與函數(shù)返回值類(lèi)型,并添加其類(lèi)與命名空間的關(guān)聯(lián)集到集合。 7)?對(duì)于指向類(lèi) X 成員函數(shù) F 的指針類(lèi)型參數(shù),檢驗(yàn)函數(shù)參數(shù)類(lèi)型、函數(shù)返回值類(lèi)型及類(lèi) X ,并添加其類(lèi)與命名空間的關(guān)聯(lián)集到集合。 8)?對(duì)于指向類(lèi) X 數(shù)據(jù)成員 T 的指針類(lèi)型參數(shù),檢驗(yàn)成員類(lèi)型和類(lèi)型 X ,并添加其類(lèi)與命名空間的關(guān)聯(lián)集到集合。 9)?若參數(shù)是重載函數(shù)集的取址表達(dá)式(或?qū)瘮?shù)模板)的名稱(chēng),則檢驗(yàn)重載集中的每個(gè)元素,并添加其類(lèi)與命名空間的關(guān)聯(lián)集到集合。 a)?另外,若重載集為模板 id (帶模板實(shí)參的模板名)所指名,則檢驗(yàn)其所有類(lèi)型模板實(shí)參與模板模板實(shí)參(但無(wú)非類(lèi)型模板實(shí)參),并添加其類(lèi)與命名空間的關(guān)聯(lián)集到集合。若類(lèi)與命名空間的關(guān)聯(lián)集中的任何命名空間是內(nèi)聯(lián)命名空間,則添加其外圍命名空間到集合。
若類(lèi)與命名空間的關(guān)聯(lián)集中的任何命名空間直接含有內(nèi)聯(lián)命名空間,則添加該內(nèi)聯(lián)命名空間到集合。
在確定命名空間與類(lèi)的關(guān)聯(lián)集后,為了進(jìn)一步的 ADL 處理,忽略此集中所有于類(lèi)中找到的聲明,除了命名空間作用域的友元函數(shù)及函數(shù)模板,陳述于后述點(diǎn) 2 。
以下列特殊規(guī)則,合并普通非限定查找找到的聲明集合,與在 ADL 所生成關(guān)聯(lián)集的所有元素中找到的聲明集合
1)?忽略關(guān)聯(lián)命名空間中的?using 指令 2)?聲明于關(guān)聯(lián)類(lèi)中的命名空間作用域友元函數(shù)(及函數(shù)模板)通過(guò) ADL 可見(jiàn),即使它們通過(guò)普通查找不可見(jiàn)。 3)?忽略函數(shù)與函數(shù)模板外的所有名稱(chēng)(與變量不沖突)注意
因?yàn)閰?shù)依賴(lài)查找,定義于相同命名空間的非成員函數(shù)和非成員運(yùn)算符被認(rèn)為是該類(lèi)公開(kāi)接口的一部分(若它們?yōu)?ADL 所找到)[1]。
ADL 是為于泛型代碼交換二個(gè)對(duì)象而建立的手法的背后理由:
using std::swap; swap(obj1, obj2);因?yàn)橹苯诱{(diào)用?std::swap(obj1, obj2)?不會(huì)考慮用戶(hù)定義的 swap() 函數(shù),它可能定義于與 obj1 或 obj2 類(lèi)型之定義相同的空間,而僅調(diào)用非限定的?swap(obj1, obj2)?會(huì)無(wú)法調(diào)用任何函數(shù),若不提供用戶(hù)定義重載。特別是?std::iter_swap?與所有其他標(biāo)準(zhǔn)庫(kù)算法在處理可交換?(Swappable) 類(lèi)型時(shí)使用此手段。
名稱(chēng)查找規(guī)則使得在來(lái)自 std 命名空間的類(lèi)型上聲明運(yùn)算符于全局或用戶(hù)定義命名空間,例如對(duì)于?std::vector?或?std::pair?的自定義?operator+?或?operator>>?不適于實(shí)踐(除非 vector/pair 的元素類(lèi)型是用戶(hù)定義類(lèi)型,這會(huì)添加其命名空間到 ADL )。這種運(yùn)算符不會(huì)從諸如標(biāo)準(zhǔn)庫(kù)算法的模板實(shí)例化查找。進(jìn)一步細(xì)節(jié)見(jiàn)依賴(lài)名。
ADL 能找到全體定義于類(lèi)或類(lèi)模板內(nèi)的友元函數(shù)(典型地是重載的運(yùn)算符),即使它完全不在命名空間層次聲明。
1 template<typename T> 2 struct number 3 { 4 number(int); 5 friend number gcd(number x, number y) { return 0; }; // 類(lèi)模板內(nèi)的定義 6 }; 7 // 除非提供匹配聲明,否則 gcd 是此命名空間的不可見(jiàn)成員(除非通過(guò) ADL ) 8 void g() { 9 number<double> a(3), b(4); 10 a = gcd(a,b); // 找到 gcd ,因?yàn)?number<double> 是關(guān)聯(lián)類(lèi), 11 // 令 gcd 于其命名空間(全局命名空間)可見(jiàn) 12 // b = gcd(3,4); // 錯(cuò)誤: gcd 不可見(jiàn) 13 }盡管即使普通查找找不到結(jié)果,函數(shù)調(diào)用也能通過(guò) ADL 解決,對(duì)帶顯示指定模板實(shí)參的函數(shù)模板調(diào)用還是要求有普通查找所能找到的模板聲明(否則,它會(huì)是遇到未知名稱(chēng)后隨小于號(hào)的語(yǔ)法錯(cuò)誤)
1 namespace N1 { 2 struct S {}; 3 template<int X> void f(S); 4 } 5 namespace N2 { 6 template<class T> void f(T t); 7 } 8 void g(N1::S s) { 9 f<3>(s); // 語(yǔ)法錯(cuò)誤(無(wú)限定查找找不到 f ) 10 N1::f<3>(s); // OK ,有限定查找找到模板 'f' 11 N2::f<3>(s); // 錯(cuò)誤: N2::f 不接收非類(lèi)型模板形參 12 // N1::f 不能被找到,因?yàn)?ADL 僅適用于非限定名 13 using N2::f; 14 f<3>(s); // OK :無(wú)限定查找現(xiàn)在找到 N2::f 然后 ADL 表態(tài), 15 // 因?yàn)榇嗣麩o(wú)限定并找到 N1::f 16 }下列語(yǔ)境發(fā)生僅 ADL 的查找(即僅于關(guān)聯(lián)的命名空間查找):
- 范圍 for?循環(huán)查找非成員函數(shù)?begin?與?end?,若成員查找失敗
- 從模板實(shí)例化點(diǎn)的依賴(lài)名查找。
- 結(jié)構(gòu)化綁定聲明為類(lèi)tuple類(lèi)型查找非成員函數(shù)get(c++17起)
?
示例
2 struct X;
3 struct Y;
4 void f(int);
5 void g(X);
6 }
7
8 namespace B {
9 void f(int i) {
10 f(i); // 調(diào)用 B::f (無(wú)限遞歸)
11 }
12 void g(A::X x) {
13 g(x); // 錯(cuò)誤:在 B::g (普通查找)與 A::g (參數(shù)依賴(lài)查找)間歧義
14 }
15 void h(A::Y y) {
16 h(y); // 調(diào)用 B::h (無(wú)限遞歸): ADL 檢驗(yàn) A 命名空間
17 // 但找不到 A::h ,故只用來(lái)自通常查找的 B::h
18 }
19 } ?
轉(zhuǎn)載于:https://www.cnblogs.com/zl1991/p/7718718.html
總結(jié)
以上是生活随笔為你收集整理的参数依赖查找(ADL,Argument-dependent lookup)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 梦到结婚好不好周公解梦
- 下一篇: 2016 年 ACM/ICPC 青岛区域