顧名思義,typename有雙重含意。只要你用過template,那麼第一重含意必定知道,那就是聲明模板的時候,咱們既能夠這樣寫:函數
template <class T> spa
也能夠這樣寫翻譯
template <typename T> code
這兩種寫法並無任何區別,都是標記T能夠是符合隱式接口的任何類型,包括系統預約義類型,也包括用戶自定義類型。對象
typename的第二重含意其實不大能遇到,由於這個依賴於編譯器,看下面的例子:blog
1 class SampleClass 2 { 3 public: 4 typedef int MyInt; 5 // static const int MyInt = 3; 6 }; 7 8 int main() 9 { 10 SampleClass::MyInt *b = new int(3); // 有的編譯器會報錯 11 }
MyInt來源於SampleClass內的一個重定義,MyInt等價於int,因此main函數裏面實質上是int *b = new int (3),但有的編譯器會報error,這是由於編譯器在遇到這句代碼上會產生歧義,由於它能夠將MyInt做爲SampleClass的一個靜態對象來看(能夠看程序被註釋掉的代碼的地方),這樣就變成了一個靜態對象乘以b了。這種「見解」有些難以想象,但在有些編譯器上,倒是優先將之視爲靜態變量的,而不是類型。爲了解決這個二義性,在前面加上typename,這樣就會強制讓編譯器將之視爲一個類型,而不是靜態變量了。像這種SampleClass::MyInt或是書上舉的T::const_iterator,都是類中定義的名字,這種名字稱之爲dependent names(嵌套從屬名稱),全部dependent names都潛在具備二義性,爲了消除二義性,就在前面加上typename,變成typename T::const_iterator,typename SampleClass:MyInt,這樣就會解決一些看似正確卻怎麼也編不過的代碼問題了。繼承
使用typename有兩個特例,一個是繼承的時候,像下面這樣:接口
1 class A: public B::NestedClass{}; // 正確 2 class A: public typename B::NextedClass(){}; // 錯誤
在繼承XXX類時,即便這個類名是dependent names,也不要使用typename,由於編譯器這時候顯示不會將之翻譯成靜態成員(被繼承類一定是個類型)。編譯器
另外一個特例是構造函數時,對於構形成員列表,像下面這樣:it
1 A(): B::NestedClass(){} // 正確 2 A(): typename B::NestedClass(){} // 錯誤
這時候編譯器也不會將之視爲靜態對象的,由於靜態對象是不支持成員初始化列表這樣的初始化形式的,因此這時的typename,反而會被編譯器認爲一個是BUG。
最後總結一下:
1. 聲明template參數時,前綴關鍵字class與typename能夠互換
2. 請使用關鍵字typename標識嵌套從屬類型名稱;但不得在base class lists或者member initialization list內使用typename