讀書筆記_Effective_C++_條款四十二:瞭解typename的雙重意義

顧名思義,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

相關文章
相關標籤/搜索