一、符號尋找:
這裡先給出兩個基本的術語,它們將在後面的篇節中被廣泛的引用。
1. 限定範圍符號:
如果一個名稱(函數名、類名或變數名)的前麵包含域解析運算子(::),或成員訪問運算子(. or ->),這表明該名稱將屬於某一範圍,那麼我們就將該類名稱成為限定範圍符號。如:::GetTickCount()、myClass->GetCount()等。與其相反的我們則成為非限定範圍符號,如:GetCount(),GetTickCount()等。
2. 依賴型符號:
如果一個名稱依賴於某個模板參數,如:vector<T>::iterator,這裡由於T是一個模板參數,同時也是一個未知類型,因此我們可以稱iterator為依賴型符號,如果改為vector<int>::iterator,那麼這裡的iterator將不再為依賴型符號了。
C++編譯器在進行編譯時間,需要對一些符號進行尋找,以確認其身份是否合法。對於限定範圍的符號,它的尋找範圍相對有限,如類成員變數,它的尋找範圍將僅限於當前類、父類等。見如下程式碼範例和關鍵性注釋:
1 #include <stdio.h> 2 3 int i; 4 class Base { 5 public: 6 Base() : i(0) {} 7 protected: 8 int i; 9 };10 11 class Derive : public Base {12 public:13 void DoTest() {14 //符號i的搜尋範圍將僅限於當前類和其基類Base。15 printf("The value of i is %d.\n",this->i);16 }17 };18 19 int main() {20 Derive d;21 d.DoTest();22 return 0;23 }
然而對於非限定範圍符號的尋找則沒有此類的限制,基本的尋找規則為由內到外逐層尋找,即先尋找類內和父類範圍內,如果均為找到,在尋找類的外部,如果在外部仍為找到,C++編譯器將會啟用ADL(參數依賴尋找)規則。現在我們還是通過樣本來講清楚這個複雜的概念吧。
template<typename T> T const& max(T const& a, T const& b) { return a < b ? b : a; } namespace BigMath { class BigNumber { ... ... } bool operator<(BigNumber const&, BigNumber const&); } using BigMath::BigNumber; void f(BigNumber const& a, BigNumber const& b) { ... ... BigNumber c = max(a,b); }
二、ADL(Argument-Dependent Lookup):
首先明確一下,ADL只能應用於非限定範圍符號。C++編譯器的符號尋找範圍將會根據函數的實參進一步擴大,即實參所關聯的類和名字關鍵都將成為本次符號尋找的考慮範圍了。其基本規則如下:
1. 對於基本類型,其範圍為空白集。
2. 對於指標和數組,其範圍為指標或數群組類型所關聯的類和名字空間。
3. 對於枚舉類型,僅為該枚舉聲明所在的名字空間。
4. 對於類類型,將包括類本身和直接基類,以及他們所在的名字空間,如果是模板類,還將包括模板類執行個體化後模板實參所關聯的類和所在的名字空間。
5. 對於函數,將包括其實參和傳回值所關聯的類和其所屬的名字空間。
6. 對於類成員指標,除了其外圍類之外,還將包括該指標類型所關聯的類和名字空間。
綜上所述,我們就可以很容易的理解上面的程式碼範例了,由於f()函數的參數為BigNumber,編譯器在搜尋BigNumber的operator<(...)時會考慮到BigNumber所在的名字空間,這樣就可以定位到與BigNumber對應的operator<(...)函數了。
三、模板解析:
這裡著重討論的是template關鍵字的另外一種應用情境,見如下範例程式碼和關鍵性注釋:
1 #include <stdio.h> 2 3 template<typename T> 4 class InnerClass { 5 public: 6 static void DoTest() { 7 printf("This is InterClass::DoTest().\n"); 8 } 9 };10 11 template<typename T>12 class OuterClass {13 public:14 template<typename T2>15 void func() {16 T2::DoTest();17 }18 };19 20 template<typename T> 21 void DoTest(OuterClass<T>& outClass) {22 //限定符.前面的變數outClass的類型依賴於模板參數,而其後面的限定符也是一個23 //模板符號,在這種情況下,為了使編譯器能夠確切的清楚func符號的類型,我們24 //需要在.和func之間插入template關鍵字,以明確告訴編譯器func的類型。25 outClass.template func<InnerClass<T> >();26 }27 28 int main() {29 OuterClass<int> outClass;30 DoTest(outClass);31 return 0;32 }