posts - 134,comments - 22,trackbacks - 0

          ADL(Koenig) 查找

          ADL ,參數相關查找,也稱作為 Koenig 查找(以 Andrew Koenig 的名字命名),是指在編譯器對無限定域的函數調用進行名字查找時,所應用的一種查找規則。

          首先來看一個函數所在的域的分類:

          1 :類域(函數作為某個類的成員函數(靜態或非靜態))

          2 :名字空間域

          3 :全局域

          而 Koenig 查找,它的規則就是當編譯器對無限定域的函數調用進行名字查找時,除了當前名字空間域以外,也會把函數參數類型所處的名字空間 加入查找的范圍。

          ADL 就是為了確保使用類型 X 的對象 x 時能夠像使用 X 的成員函數一樣簡單 (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) 。

          根據 ADL 規則,如果非成員函數想和類型一起被使用,應該將它們置于同一個名字空間中。換句話說,和類型 X 放在同一名字空間下的非成員函數,也是 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);     // 受限函數,禁用 ADL  // 輸出 ::f(int) called

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

                 return 0;

          }

           

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

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

          調用函數時,顯式地限定名字空間將禁用 ADL 查找 ,加快解析過程。

           

          Argument Dependent Lookup (ADL)解析

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

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

          上面的函數調用,第一個f就是無限定域的函數調用,第二個則限定了在名字空間N里面,也是說使用了完全限定名。
          我們首先來看一個函數所在的域的分類:
          1:類域(函數作為某個類的成員函數(靜態或非靜態))
          2:名字空間域
          3:全局域
          而Koenig查找,它的規則就是當編譯器對無限定域的函數調用進行名字查找時,除了當前名字空間域以外,也會把函數參數類型所處的名字空間加入查找的范圍。
          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<<打印對象的狀態,但是ostream& operator<<(ostream& out, const KoenigArg& kArg) 的定義是處于名字空間Koenig,為什么編譯器在解析main函數(全局域)里面的operator<<調用時,它能夠正確定位到Koenig名字空間里面的operator<<?這是因為根據Koenig查找規則,編譯器需要把參數類型KoenigArg所在的名字空間Koenig也加入對operator<<調用的名字查找范圍中。

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

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


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

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

          一個類描述了數據的集合以及操作這些數據的函數。
          Herb的類定義,稱之為接口準則(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.

          對應類X來說,所有函數,包括自由函數,只要它們
                   提及X(跟X有關)
                   X一起提供
          都在邏輯上被認為是X的一部分,因為它們是X的接口的一部分。
          關于Koenig查找,我們該說的都說了嗎?其實未然,之前所描述的只是Koenig查找一般可能發生的狀況,當Koenig查找規則和C++ 原來的Ordinal Lookup(OL,順序查找規則)混合在一起的時候,它們之間的組合所產生的狀況要比之前的例子復雜的多……
          posted on 2009-12-13 11:37 何克勤 閱讀(386) 評論(0)  編輯  收藏 所屬分類: C/C++
          主站蜘蛛池模板: 莲花县| 炉霍县| 高唐县| 积石山| 陆良县| 惠东县| 达日县| 芜湖市| 建德市| 寻乌县| 扬中市| 乌拉特中旗| 开封县| 进贤县| 晋中市| 乡宁县| 句容市| 丹凤县| 华容县| 杨浦区| 乐都县| 长垣县| 西城区| 临泽县| 阳西县| 调兵山市| 宜君县| 泰兴市| 天门市| 佛教| 崇仁县| 从江县| 晋城| 高安市| 昭平县| 江川县| 江口县| 独山县| 永仁县| 东方市| 阳山县|