在 C++模板函數(shù)的使用過程中,我們經(jīng)常可以看到一個 typename 的使用,例如這樣的操作
但是除此之外,我們也會經(jīng)??吹竭@樣的用法
那么這里就要問大家,這 C++類似的用法下有什么區(qū)別呢,且聽我細細道來。
由來分析
"typename"是一個 C++程序設計語言中的關鍵字。當用于泛型編程時是另一術語"class"的同義詞。這個關鍵字用于指出模板聲明(或定義)中的非獨立名稱(dependent names)是類型名,而非變量名。
我們經(jīng)常會這么用 typename,這是一項 C++編程語言的泛型編程(或曰“模板編程”)的功能,typename 關鍵字用于引入一個模板參數(shù)。
template
const T& max(const T& x, const T& y)
{
if (y < x) {
return x;
}
return y;
}
在模板定義語法中關鍵字 class 與 typename 的作用完全一樣
template
const T& max(const T& x, const T& y)
{
if (y < x) {
return x;
}
return y;
}
這里 class
關鍵字表明 T 是一個類型,后來為了避免 class
在這兩個地方的使用可能給人帶來混淆,所以引入了 typename
這個關鍵字,它的作用同 class
一樣表明后面的符號為一個類型。
那 class 使用就夠了,為什么又引入了新的關鍵詞 typename ,關于這個問題,Stan Lippman 曾在其博客中表示,最早 Stroustrup 使用class
來聲明模板參數(shù)列表中的類型是為了避免增加不必要的關鍵字;后來委員會認為這樣混用可能造成概念上的混淆才加上了typename
關鍵字。
而使用 typename
的作用就是告訴 c++ 編譯器,typename
后面的字符串為一個類型名稱,而不是成員函數(shù)或者成員變量,這個時候如果前面沒有 typename
,編譯器沒有任何辦法知道 T::LengthType 是一個類型還是一個成員名稱(靜態(tài)數(shù)據(jù)成員或者靜態(tài)函數(shù)),所以編譯不能夠通過。
問題浮現(xiàn)
那么問題來了,什么情況下,class 定義之后,編譯不能通過呢?
template
voidfun(constT&proto){
T::const_iteratorit(proto.begin());
}
發(fā)生編譯錯誤是因為編譯器不知道T::const_iterator
是個類型。萬一它是個變量呢?T::const_iterator
的解析有著邏輯上的矛盾: 直到確定了T
是什么東西,編譯器才會知道T::const_iterator
是不是一個類型; 然而當模板被解析時,T
還是不確定的。這時我們聲明它為一個類型才能通過編譯:
而且在模板實例化之前,完全沒有辦法來區(qū)分它們,這絕對是滋生各種 bug 的溫床。這時 C++標準委員會再也忍不住了,與其到實例化時才能知道到底選擇哪種方式來解釋以上代碼,委員會決定引入一個新的關鍵字,這就是typename
。
千呼萬喚始出來,我們來看看 C++標準:
對于用于模板定義的依賴于模板參數(shù)的名稱,只有在實例化的參數(shù)中存在這個類型名,或者這個名稱前使用了typename
關鍵字來修飾,編譯器才會將該名稱當成是類型。除了以上這兩種情況,絕不會被當成是類型。
因此,如果你想直接告訴編譯器T::const_iterator
是類型而不是變量,只需用typename
修飾:
typenameT::const_iteratorit(proto.begin());
這樣編譯器就可以確定T::const_iterator
是一個類型,而不再需要等到實例化時期才能確定,因此消除了前面提到的歧義。
嵌套從屬類型
事實上類型T::const_iterator
依賴于模板參數(shù)T
, 模板中依賴于模板參數(shù)的名稱稱為從屬名稱(dependent name), 當一個從屬名稱嵌套在一個類里面時,稱為嵌套從屬名稱(nested dependent name)。 其實T::const_iterator
還是一個嵌套從屬類型名稱(nested dependent type name)。
嵌套從屬名稱是需要用typename
聲明的,其他的名稱是不可以用typename
聲明的。比如下面是一個合法的聲明:
template
voidfun(constT&proto,typenameT::const_iteratorit);
使用
在定義類模板或者函數(shù)模板時,typename
和class
關鍵字都可以用于指定模板參數(shù)中的類型。也就是說,以下兩種用法是完全等價的。
template
/*... */; template
/* ... */;
既然typename
關鍵字已經(jīng)存在,而且它也可以用于最常見的指定模板參數(shù),那么為什么不廢除class
這一用法呢?答案其實也很明顯,因為在最終的標準出來之前,所有已存在的書、文章、教學、代碼中都是使用的是class
,可以想像,如果標準不再支持class
,會出現(xiàn)什么情況。
使用關鍵字typename
代替關鍵字class
指定模板類型形參更為直觀,畢竟,可以使用內(nèi)置類型(非類類型)作為實際的類型形參,而且,typename
更清楚地指明后面的名字是一個類型名。但是,關鍵字typename
是作為標準 C++的組成部分加入到 C++中的,因此舊的程序更有可能只用關鍵字class
。
這就是我分享的 c++的 typename,此外如果大家有什么更好的思路,也歡迎分享交流哈。