posts - 134,comments - 22,trackbacks - 0

          ADL(Koenig) 查找

          ADL ,參數(shù)相關(guān)查找,也稱作為 Koenig 查找(以 Andrew Koenig 的名字命名),是指在編譯器對無限定域的函數(shù)調(diào)用進(jìn)行名字查找時(shí),所應(yīng)用的一種查找規(guī)則。

          首先來看一個(gè)函數(shù)所在的域的分類:

          1 :類域(函數(shù)作為某個(gè)類的成員函數(shù)(靜態(tài)或非靜態(tài)))

          2 :名字空間域

          3 :全局域

          而 Koenig 查找,它的規(guī)則就是當(dāng)編譯器對無限定域的函數(shù)調(diào)用進(jìn)行名字查找時(shí),除了當(dāng)前名字空間域以外,也會把函數(shù)參數(shù)類型所處的名字空間 加入查找的范圍。

          ADL 就是為了確保使用類型 X 的對象 x 時(shí)能夠像使用 X 的成員函數(shù)一樣簡單 (ensure that code that uses an object x of type X can use its nonmember function interface as easily as it can use member functions) 。

          根據(jù) ADL 規(guī)則,如果非成員函數(shù)想和類型一起被使用,應(yīng)該將它們置于同一個(gè)名字空間中。換句話說,和類型 X 放在同一名字空間下的非成員函數(shù),也是 X 接口的一部分。 ( 常見的有 <<or>> 操作符 )

          示例:

          #include <iostream>

          //class N     // 類域

          //{

          //public :

          //     enum E {e1};

          //     void f(E)

          //     {

          //            std::cout << "N::f(N::E) called\n";

          //     }

          //};

          namespace N{   // 名字空間域

                 enum E {e1};

                 void f(E){

                        std::cout << "N::f(N::E) called\n";

                 }

          }

          void f(int)

          {

                 std::cout << " ::f(int) called\n";

          }

           

          int main()

          {

                 ::f(N::e1);     // 受限函數(shù),禁用 ADL  // 輸出 ::f(int) called

                 f(N::e1);      // 普通查找到 f();

                 return 0;

          }

           

          上述例子,如果 N 為 class ,輸出為: ::f(int) called ;如果 N 為名字空間,輸出: N::f(N::E) called 。

          也就是說 ADL 僅會將參數(shù)所在的名字空間中的函數(shù)名字加入查找范圍。

          調(diào)用函數(shù)時(shí),顯式地限定名字空間將禁用 ADL 查找 ,加快解析過程。

           

          Argument Dependent Lookup (ADL)解析

          ADL,參數(shù)相關(guān)查找,也稱作為Koenig查找(以Andrew Koenig的名字命名,有興趣可以看Scott Meyer的文章The Most Important C++ People...Ever),是指在編譯器對無限定域的函數(shù)調(diào)用進(jìn)行名字查找時(shí),所應(yīng)用的一種查找規(guī)則。

          f(x, y, z); // unqualified
          N::f(x, y, z); // qualified

          上面的函數(shù)調(diào)用,第一個(gè)f就是無限定域的函數(shù)調(diào)用,第二個(gè)則限定了在名字空間N里面,也是說使用了完全限定名。
          我們首先來看一個(gè)函數(shù)所在的域的分類:
          1:類域(函數(shù)作為某個(gè)類的成員函數(shù)(靜態(tài)或非靜態(tài)))
          2:名字空間域
          3:全局域
          而Koenig查找,它的規(guī)則就是當(dāng)編譯器對無限定域的函數(shù)調(diào)用進(jìn)行名字查找時(shí),除了當(dāng)前名字空間域以外,也會把函數(shù)參數(shù)類型所處的名字空間加入查找的范圍。
          Herb提供的解釋(Exceptional C++, Item 31)

          Koenig Lookup(simplified): If you supply a function argument of class type (here x, of type A::X), then to look up the correct function name the compiler considers matching names in the namespace (here A) containing the argument's type.

          請看下面的例程:
          #include <iostream>
          usingnamespace std;

          namespace Koenig
          {
              class KoenigArg
              
          {
              public:
                    ostream
          & print(ostream&out) const
                   
          {
                           out<<member_<<endl;
                    }


                    KoenigArg(
          int member =5) : member_(member){}

              
          private:
                   
          int member_;
               }
          ;


               inline ostream
          &operator<<(ostream&out, const KoenigArg& kArg)
              
          {
                   return kArg.print(out);
               }

          }


          int main()
          {
               Koenig::KoenigArg karg(10);
               cout
          <<karg;

              
          char c;cin>>c;

              
          return0;
          }

          我們通常都會寫如上的代碼,使用operator<<打印對象的狀態(tài),但是ostream& operator<<(ostream& out, const KoenigArg& kArg) 的定義是處于名字空間Koenig,為什么編譯器在解析main函數(shù)(全局域)里面的operator<<調(diào)用時(shí),它能夠正確定位到Koenig名字空間里面的operator<<?這是因?yàn)楦鶕?jù)Koenig查找規(guī)則,編譯器需要把參數(shù)類型KoenigArg所在的名字空間Koenig也加入對operator<<調(diào)用的名字查找范圍中。

          如果沒有Koenig查找規(guī)則,我們就無法直接寫cout<<karg;,而是需要寫類似Koenig::operator<<(std::cout, karg); 這樣的代碼(使用完全限定名)。嗯,即不直觀也不方便是嗎?更重要的是如果我們寫的是模版代碼,在模版參數(shù)還沒有實(shí)例化之前,我們根本就不知道參數(shù)所處的名字空間,比如:

          template<typename T>void print(const T& value)
          {
               std::cout<<value;
          }


          print(karg);
          很顯然,你的模版代碼根本無法確認(rèn)T是來自那個(gè)名字空間,直到編譯器對模版實(shí)例化(print(karg); 被調(diào)用)。
          對Koenig查找規(guī)則的一個(gè)異議是,由于Koenig查找規(guī)則的存在,處于某個(gè)名字空間的函數(shù)調(diào)用的重載決議會受到另外一個(gè)名字空間的自由函數(shù)所影響,僅僅是由于它使用了另外一個(gè)名字空間的類型作為參數(shù)。在這樣的規(guī)則下,名字空間看起來不像我們一般所想象的那樣是完全封閉和獨(dú)立的。
          我們應(yīng)該怎么解釋這樣的異議呢?這樣隱諱的影響或者依賴性是合理的嗎?Herb認(rèn)為,如果我們把另外一個(gè)名字空間的自由函數(shù)(非類成員函數(shù))也看作是它所涉及的類型的接口的一部分,很顯然,它應(yīng)該參與這樣的重載決議,這樣的跨越名字空間的影響是合理的。從而導(dǎo)出了Herb在傳統(tǒng)類定義之上的一個(gè)更詳細(xì)和完整的解釋(請參考Exceptional C++, Item 32)。
          傳統(tǒng)的類定義:

          A class describes a set of data, along with the functions that operate on that data.

          一個(gè)類描述了數(shù)據(jù)的集合以及操作這些數(shù)據(jù)的函數(shù)。
          Herb的類定義,稱之為接口準(zhǔn)則(Interface Principle):
          For a class X, all functions, including free functions, that both
          "Mention" X
          Are "supplied with" X

          are logically part of X, because they form part of the interface of X.

          對應(yīng)類X來說,所有函數(shù),包括自由函數(shù),只要它們
                   提及X(跟X有關(guān))
                   X一起提供
          都在邏輯上被認(rèn)為是X的一部分,因?yàn)樗鼈兪?/span>X的接口的一部分。
          關(guān)于Koenig查找,我們該說的都說了嗎?其實(shí)未然,之前所描述的只是Koenig查找一般可能發(fā)生的狀況,當(dāng)Koenig查找規(guī)則和C++ 原來的Ordinal Lookup(OL,順序查找規(guī)則)混合在一起的時(shí)候,它們之間的組合所產(chǎn)生的狀況要比之前的例子復(fù)雜的多……
          posted on 2009-12-13 11:37 何克勤 閱讀(386) 評論(0)  編輯  收藏 所屬分類: C/C++
          主站蜘蛛池模板: 十堰市| 亳州市| 凌源市| 林周县| 宜都市| 淮南市| 新闻| 万年县| 汾阳市| 德庆县| 改则县| 都昌县| 玉环县| 驻马店市| 嘉义市| 安平县| 绥芬河市| 凌云县| 蕲春县| 邹平县| 肥城市| 丰县| 双柏县| 亚东县| 鹤山市| 台山市| 突泉县| 安康市| 雷州市| 嵊州市| 仪陇县| 梅河口市| 沙坪坝区| 洞头县| 赤壁市| 金阳县| 左贡县| 邢台县| 乳源| 新巴尔虎左旗| 丽江市|