C++箴言:理解typename的兩個(gè)含義

字號(hào):

問題:在下面的 template declarations(模板聲明)中 class 和 typename 有什么不同?
     template class Widget; // uses "class"
     template class Widget; // uses "typename"
    答案:沒什么不同。在聲明一個(gè) template type parameter(模板類型參數(shù))的時(shí)候,class 和 typename 意味著完全相同的東西。一些程序員更喜歡在所有的時(shí)間都用 class,因?yàn)樗菀纵斎搿F渌耍òㄎ冶救耍└矚g typename,因?yàn)樗凳局@個(gè)參數(shù)不必要是一個(gè) class type(類類型)。少數(shù)開發(fā)者在任何類型都被允許的時(shí)候使用 typename,而把 class 保留給僅接受 user-defined types(用戶定義類型)的場合。但是從 C++ 的觀點(diǎn)看,class 和 typename 在聲明一個(gè) template parameter(模板參數(shù))時(shí)意味著完全相同的東西。
    然而,C++ 并不總是把 class 和 typename 視為等同的東西。有時(shí)你必須使用 typename。為了理解這一點(diǎn),我們不得不討論你會(huì)在一個(gè) template(模板)中涉及到的兩種名字。
    假設(shè)我們有一個(gè)函數(shù)的模板,它能取得一個(gè) STL-compatible container(STL 兼容容器)中持有的能賦值給 ints 的對象。進(jìn)一步假設(shè)這個(gè)函數(shù)只是簡單地打印它的第二個(gè)元素的值。它是一個(gè)用糊涂的方法實(shí)現(xiàn)的糊涂的函數(shù),而且就像我下面寫的,它甚至不能編譯,但是請將這些事先放在一邊——有一種方法能發(fā)現(xiàn)我的愚蠢:
     template // print 2nd element in
     void print2nd(const C& container) // container;
     {
    // this is not valid C++!
    if (container.size() >= 2) {
    C::const_iterator iter(container.begin()); // get iterator to 1st element
    ++iter; // move iter to 2nd element
    int value = *iter; // copy that element to an int
    std::cout << value; // print the int
    }
     }
    我突出了這個(gè)函數(shù)中的兩個(gè) local variables(局部變量),iter 和 value。iter 的類型是 C::const_iterator,一個(gè)依賴于 template parameter(模板參數(shù))C 的類型。一個(gè) template(模板)中的依賴于一個(gè) template parameter(模板參數(shù))的名字被稱為 dependent names(依賴名字)。當(dāng)一個(gè) dependent names(依賴名字)嵌套在一個(gè) class(類)的內(nèi)部時(shí),我稱它為 nested dependent name(嵌套依賴名字)。C::const_iterator 是一個(gè) nested dependent name(嵌套依賴名字)。實(shí)際上,它是一個(gè) nested dependent type name(嵌套依賴類型名),也就是說,一個(gè)涉及到一個(gè) type(類型)的 nested dependent name(嵌套依賴名字)。
    print2nd 中的另一個(gè) local variable(局部變量)value 具有 int 類型。int 是一個(gè)不依賴于任何 template parameter(模板參數(shù))的名字。這樣的名字以 non-dependent names(非依賴名字)聞名。(我想不通為什么他們不稱它為 independent names(無依賴名字)。如果,像我一樣,你發(fā)現(xiàn)術(shù)語 "non-dependent" 是一個(gè)令人厭惡的東西,你就和我產(chǎn)生了共鳴,但是 "non-dependent" 就是這類名字的術(shù)語,所以,像我一樣,轉(zhuǎn)轉(zhuǎn)眼睛放棄你的自我主張。)
    nested dependent name(嵌套依賴名字)會(huì)導(dǎo)致解析困難。例如,假設(shè)我們更加愚蠢地以這種方法開始 print2nd:
     template
     void print2nd(const C& container)
     {
    C::const_iterator * x;
    ...
     }
    這看上去好像是我們將 x 聲明為一個(gè)指向 C::const_iterator 的 local variable(局部變量)。但是它看上去如此僅僅是因?yàn)槲覀冎?C::const_iterator 是一個(gè) type(類型)。但是如果 C::const_iterator 不是一個(gè) type(類型)呢?如果 C 有一個(gè) static data member(靜態(tài)數(shù)據(jù)成員)碰巧就叫做 const_iterator 呢?再如果 x 碰巧是一個(gè) global variable(全局變量)的名字呢?在這種情況下,上面的代碼就不是聲明一個(gè) local variable(局部變量),而是成為 C::const_iterator 乘以 x!當(dāng)然,這聽起來有些愚蠢,但它是可能的,而編寫 C++ 解析器的人必須考慮所有可能的輸入,甚至是愚蠢的。
    直到 C 成為已知之前,沒有任何辦法知道 C::const_iterator 到底是不是一個(gè) type(類型),而當(dāng) template(模板)print2nd 被解析的時(shí)候,C 還不是已知的。C++ 有一條規(guī)則解決這個(gè)歧義:如果解析器在一個(gè) template(模板)中遇到一個(gè) nested dependent name(嵌套依賴名字),它假定那個(gè)名字不是一個(gè) type(類型),除非你用其它方式告訴它。缺省情況下,nested dependent name(嵌套依賴名字)不是 types(類型)。(對于這條規(guī)則有一個(gè)例外,我待會(huì)兒告訴你。)
    記住這個(gè),再看看 print2nd 的開頭:
     template
     void print2nd(const C& container)
     {
    if (container.size() >= 2) {
    C::const_iterator iter(container.begin()); // this name is assumed to
    ... // not be a type