Step By Step(C++模板解析)

[来源] 达内    [编辑] 达内   [时间]2012-09-13

如果一个名称(函数名、类名或变量名)的前面包含域解析运算符(::),或成员访问运算符(. or ->),这表明该名称将属于某一作用域,那么我们就将该类名称成为限定作用域符号

一、符号查找:

    这里先给出两个基本的术语,它们将在后面的篇节中被广泛的引用。
    1. 限定作用域符号:
    如果一个名称(函数名、类名或变量名)的前面包含域解析运算符(::),或成员访问运算符(. or ->),这表明该名称将属于某一作用域,那么我们就将该类名称成为限定作用域符号。如:::GetTickCount()、myClass->GetCount()等。与其相反的我们则成为非限定作用域符号,如:GetCount(),GetTickCount()等。
    
    2. 依赖型符号:
    如果一个名称依赖于某个模板参数,如:vector<T>::iterator,这里由于T是一个模板参数,同时也是一个未知类型,因此我们可以称iterator为依赖型符号,如果改为vector<int>::iterator,那么这里的iterator将不再为依赖型符号了。
    C++编译器在进行编译时,需要对一些符号进行查找,以确认其身份是否合法。对于限定作用域的符号,它的查找范围相对有限,如类成员变量,它的查找范围将仅限于当前类、父类等。见如下代码示例和关键性注释:

< div style="background-color: rgb(245, 245, 245); font-family: 'Courier New'; font-size: 12px; border: 1px solid rgb(204, 204, 204); padding: 5px; overflow: auto; margin: 5px 0px; max-width: 900px; color: rgb(0, 0, 0); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 18px; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class="cnblogs_code">
 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     }
< p style="margin: 5px auto; text-indent: 0px; color: rgb(0, 0, 0); font-family: Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 18px; orphans: 2; text-align: -webkit-auto; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); ">    然而对于非限定作用域符号的查找则没有此类的限制,基本的查找规则为由内到外逐层查找,即先查找类内和父类范围内,如果均为找到,在查找类的外部,如果在外部仍为找到,C++编译器将会启用ADL(参数依赖查找)规则。现在我们还是通过示例来讲清楚这个复杂的概念吧。

< div style="background-color: rgb(245, 245, 245); font-family: 'Courier New'; font-size: 12px; border: 1px solid rgb(204, 204, 204); padding: 5px; overflow: auto; margin: 5px 0px; max-width: 900px; color: rgb(0, 0, 0); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 18px; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class="cnblogs_code">

    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 BigMathigNumber;     
void

 f(BigNumber const& a, BigNumber const
& b) {         ... ...          BigNumber c = max(a,b);     }
< p style="margin: 5px auto; text-indent: 0px; color: rgb(0, 0, 0); font-family: Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 18px; orphans: 2; text-align: -webkit-auto; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); ">二、ADL(Argument-Dependent Lookup):
    
    首先明确一下,ADL只能应用于非限定作用域符号。C++编译器的符号查找范围将会根据函数的实参进一步扩大,即实参所关联的类和名字关键都将成为本次符号查找的考虑范围了。其基本规则如下:
    1. 对于基本类型,其范围为空集。
    2. 对于指针和数组,其范围为指针或数组类型所关联的类和名字空间。
    3. 对于枚举类型,仅为该枚举声明所在的名字空间。
    4. 对于类类型,将包括类本身和直接基类,以及他们所在的名字空间,如果是模板类,还将包括模板类实例化后模板实参所关联的类和所在的名字空间。
    5. 对于函数,将包括其实参和返回值所关联的类和其所属的名字空间。
    6. 对于类成员指针,除了其外围类之外,还将包括该指针类型所关联的类和名字空间。
    综上所述,我们就可以很容易的理解上面的代码示例了,由于f()函数的参数为BigNumber,编译器在搜索BigNumber的operator<(...)时会考虑到BigNumber所在的名字空间,这样就可以定位到与BigNumber对应的operator<(...)函数了。
    
三、模板解析:

    这里着重讨论的是template关键字的另外一种应用场景,见如下示例代码和关键性注释:

< div style="background-color: rgb(245, 245, 245); font-family: 'Courier New'; font-size: 12px; border: 1px solid rgb(204, 204, 204); padding: 5px; overflow: auto; margin: 5px 0px; max-width: 900px; color: rgb(0, 0, 0); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 18px; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class="cnblogs_code">
 1
     #include <stdio.h>
 2     
 3     template<typename T>
 4     class
 InnerClass {  5
     public

:  6
         static void
 DoTest() {  

 7             printf("
This is InterClassoTest().\n"
);  

 8         } 


 9     }; 10
     
11     template<typename T>
12     class
 OuterClass { 13
     public

: 14
         template<typename T2>
15         void
 func() { 16
             T2oTest(); 

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     }

资源下载