引 :https://www.jb51.net/article/37565.htm程序員
問題:在下面的 template declarations(模板聲明)中 class 和 typename 有什麼不一樣? ide
答案:沒什麼不一樣。在聲明一個 template type parameter(模板類型參數)的時候,class 和 typename 意味着徹底相同的東西。一些程序員更喜歡在全部的時間都用 class,由於它更容易輸入。其餘人(包括我本人)更喜歡 typename,由於它暗示着這個參數沒必要要是一個 class type(類類型)。少數開發者在任何類型都被容許的時候使用 typename,而把 class 保留給僅接受 user-defined types(用戶定義類型)的場合。可是從 C++ 的觀點看,class 和 typename 在聲明一個 template parameter(模板參數)時意味着徹底相同的東西。
然而,C++ 並不老是把 class 和 typename 視爲等同的東西。有時你必須使用 typename。爲了理解這一點,咱們不得不討論你會在一個 template(模板)中涉及到的兩種名字。
假設咱們有一個函數的模板,它能取得一個 STL-compatible container(STL 兼容容器)中持有的能賦值給 ints 的對象。進一步假設這個函數只是簡單地打印它的第二個元素的值。它是一個用糊塗的方法實現的糊塗的函數,並且就像我下面寫的,它甚至不能編譯,可是請將這 些事先放在一邊——有一種方法能發現個人愚蠢:函數
我突出了這個函數中的兩個 local variables(局部變量),iter 和 value。iter 的類型是 C::const_iterator,一個依賴於 template parameter(模板參數)C 的類型。一個 template(模板)中的依賴於一個 template parameter(模板參數)的名字被稱爲 dependent names(依賴名字)。當一個 dependent names(依賴名字)嵌套在一個 class(類)的內部時,我稱它爲 nested dependent name(嵌套依賴名字)。C::const_iterator 是一個 nested dependent name(嵌套依賴名字)。實際上,它是一個 nested dependent type name(嵌套依賴類型名),也就是說,一個涉及到一個 type(類型)的 nested dependent name(嵌套依賴名字)。
print2nd 中的另外一個 local variable(局部變量)value 具備 int 類型。int 是一個不依賴於任何 template parameter(模板參數)的名字。這樣的名字以 non-dependent names(非依賴名字)聞名。(我想不通爲何他們不稱它爲 independent names(無依賴名字)。若是,像我同樣,你發現術語 "non-dependent" 是一個使人厭惡的東西,你就和我產生了共鳴,可是 "non-dependent" 就是這類名字的術語,因此,像我同樣,轉轉眼睛放棄你的自我主張。)
nested dependent name(嵌套依賴名字)會致使解析困難。例如,假設咱們更加愚蠢地以這種方法開始 print2nd:ui
這看上去好像是咱們將 x 聲明爲一個指向 C::const_iterator 的 local variable(局部變量)。可是它看上去如此僅僅是由於咱們知道 C::const_iterator 是一個 type(類型)。可是若是 C::const_iterator 不是一個 type(類型)呢?若是 C 有一個 static data member(靜態數據成員)碰巧就叫作 const_iterator 呢?再若是 x 碰巧是一個 global variable(全局變量)的名字呢?在這種狀況下,上面的代碼就不是聲明一個 local variable(局部變量),而是成爲 C::const_iterator 乘以 x!固然,這聽起來有些愚蠢,但它是可能的,而編寫 C++ 解析器的人必須考慮全部可能的輸入,甚至是愚蠢的。
直到 C 成爲已知以前,沒有任何辦法知道 C::const_iterator 究竟是不是一個 type(類型),而當 template(模板)print2nd 被解析的時候,C 還不是已知的。C++ 有一條規則解決這個歧義:若是解析器在一個 template(模板)中遇到一個 nested dependent name(嵌套依賴名字),它假定那個名字不是一個 type(類型),除非你用其它方式告訴它。缺省狀況下,nested dependent name(嵌套依賴名字)不是 types(類型)。(對於這條規則有一個例外,我待會兒告訴你。)
記住這個,再看看 print2nd 的開頭:this
這爲何不是合法的 C++ 如今應該很清楚了。iter 的 declaration(聲明)僅僅在 C::const_iterator 是一個 type(類型)時纔有意義,可是咱們沒有告訴 C++ 它是,而 C++ 就假定它不是。要想轉變這個形勢,咱們必須告訴 C++ C::const_iterator 是一個 type(類型)。咱們將 typename 放在緊挨着它的前面來作到這一點:.net
通用的規則很簡單:在你涉及到一個在 template(模板)中的 nested dependent type name(嵌套依賴類型名)的任什麼時候候,你必須把單詞 typename 放在緊挨着它的前面。(重申一下,我待會兒要描述一個例外。)
typename 應該僅僅被用於標識 nested dependent type name(嵌套依賴類型名);其它名字不該該用它。例如,這是一個取得一個 container(容器)和這個 container(容器)中的一個 iterator(迭代器)的 function template(函數模板):code
C 不是一個 nested dependent type name(嵌套依賴類型名)(它不是嵌套在依賴於一個 template parameter(模板參數)的什麼東西內部的),因此在聲明 container 時它沒必要被 typename 前置,可是 C::iterator 是一個 nested dependent type name(嵌套依賴類型名),因此它必需被 typename 前置。
"typename must precede nested dependent type names"(「typename 必須前置於嵌套依賴類型名」)規則的例外是 typename 沒必要前置於在一個 list of base classes(基類列表)中的或者在一個 member initialization list(成員初始化列表)中做爲一個 base classes identifier(基類標識符)的 nested dependent type name(嵌套依賴類型名)。例如:htm
這樣的矛盾很使人討厭,可是一旦你在經歷中得到一點經驗,你幾乎不會在乎它。
讓咱們來看最後一個 typename 的例子,由於它在你看到的真實代碼中具備表明性。假設咱們在寫一個取得一個 iterator(迭代器)的 function template(函數模板),並且咱們要作一個 iterator(迭代器)指向的 object(對象)的局部拷貝 temp,咱們能夠這樣作:對象
不要讓 std::iterator_traits<IterT>::value_type 嚇倒你。那僅僅是一個 standard traits class(標準特性類)的使用,用 C++ 的說法就是 "the type of thing pointed to by objects of type IterT"(「被類型爲 IterT 的對象所指向的東西的類型」)。這個語句聲明瞭一個與 IterT objects 所指向的東西類型相同的 local variable(局部變量)(temp),並且用 iter 所指向的 object(對象)對 temp 進行了初始化。若是 IterT 是 vector<int>::iterator,temp 就是 int 類型。若是 IterT 是 list<string>::iterator,temp 就是 string 類型。由於 std::iterator_traits<IterT>::value_type 是一個 nested dependent type name(嵌套依賴類型名)(value_type 嵌套在 iterator_traits<IterT> 內部,並且 IterT 是一個 template parameter(模板參數)),咱們必須讓它被 typename 前置。
若是你以爲讀 std::iterator_traits<IterT>::value_type 使人討厭,就想象那個與它相同的東西來表明它。若是你像大多數程序員,對屢次輸入它感到恐懼,那麼你就須要建立一個 typedef。對於像 value_type 這樣的 traits member names(特性成員名),一個通用的慣例是 typedef name 與 traits member name 相同,因此這樣的一個 local typedef 一般定義成這樣:ci
不少程序員最初發現 "typedef typename" 並列不太和諧,但它是涉及 nested dependent type names(嵌套依賴類型名)規則的一個合理的附帶結果。你會至關快地習慣它。你畢竟有着強大的動機。你輸入 typename std::iterator_traits<IterT>::value_type 須要多少時間?做爲結束語,我應該 說起編譯器與編譯器之間對圍繞 typename 的規則的執行狀況的不一樣。一些編譯器接受必需 typename 時它卻缺失的代碼;一些編譯器接受不準 typename 時它卻存在的代碼;還有少數的(一般是老舊的)會拒絕 typename 出如今它必需出現的地方。這就意味着 typename 和 nested dependent type names(嵌套依賴類型名)的交互做用會致使一些輕微的可移植性問題。Things to Remember·在聲明 template parameters(模板參數)時,class 和 typename 是可互換的。·用 typename 去標識 nested dependent type names(嵌套依賴類型名),在 base class lists(基類列表)中或在一個 member initialization list(成員初始化列表)中做爲一個 base class identifier(基類標識符)時除外。