加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 由來分析
    • 問題浮現(xiàn)
    • 千呼萬喚始出來,我們來看看 C++標(biāo)準(zhǔn):
    • 使用
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

C++typename的由來和用法

2020/12/14
327
閱讀需 2 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

在 C++模板函數(shù)的使用過程中,我們經(jīng)??梢钥吹揭粋€(gè) typename 的使用,例如這樣的操作

但是除此之外,我們也會(huì)經(jīng)??吹竭@樣的用法

那么這里就要問大家,這 C++類似的用法下有什么區(qū)別呢,且聽我細(xì)細(xì)道來。

由來分析

"typename"是一個(gè) C++程序設(shè)計(jì)語言中的關(guān)鍵字。當(dāng)用于泛型編程時(shí)是另一術(shù)語"class"的同義詞。這個(gè)關(guān)鍵字用于指出模板聲明(或定義)中的非獨(dú)立名稱(dependent names)是類型名,而非變量名。

我們經(jīng)常會(huì)這么用 typename,這是一項(xiàng) C++編程語言的泛型編程(或曰“模板編程”)的功能,typename 關(guān)鍵字用于引入一個(gè)模板參數(shù)。

template const T& max(const T& x, const T& y){  if (y < x) {    return x;  }  return y;}

在模板定義語法中關(guān)鍵字 class 與 typename 的作用完全一樣

templateconst T& max(const T& x, const T& y){  if (y < x) {    return x;  }  return y;}

這里 class 關(guān)鍵字表明 T 是一個(gè)類型,后來為了避免 class 在這兩個(gè)地方的使用可能給人帶來混淆,所以引入了 typename 這個(gè)關(guān)鍵字,它的作用同 class 一樣表明后面的符號(hào)為一個(gè)類型。

那 class 使用就夠了,為什么又引入了新的關(guān)鍵詞 typename ,關(guān)于這個(gè)問題,Stan Lippman 曾在其博客中表示,最早 Stroustrup 使用class來聲明模板參數(shù)列表中的類型是為了避免增加不必要的關(guān)鍵字;后來委員會(huì)認(rèn)為這樣混用可能造成概念上的混淆才加上了typename關(guān)鍵字。

而使用 typename 的作用就是告訴 c++ 編譯器,typename 后面的字符串為一個(gè)類型名稱,而不是成員函數(shù)或者成員變量,這個(gè)時(shí)候如果前面沒有 typename,編譯器沒有任何辦法知道 T::LengthType 是一個(gè)類型還是一個(gè)成員名稱(靜態(tài)數(shù)據(jù)成員或者靜態(tài)函數(shù)),所以編譯不能夠通過。

問題浮現(xiàn)

那么問題來了,什么情況下,class 定義之后,編譯不能通過呢?

templatevoidfun(constT&proto){
T::const_iteratorit(proto.begin());}

發(fā)生編譯錯(cuò)誤是因?yàn)榫幾g器不知道T::const_iterator是個(gè)類型。萬一它是個(gè)變量呢?T::const_iterator的解析有著邏輯上的矛盾: 直到確定了T是什么東西,編譯器才會(huì)知道T::const_iterator是不是一個(gè)類型; 然而當(dāng)模板被解析時(shí),T還是不確定的。這時(shí)我們聲明它為一個(gè)類型才能通過編譯:

而且在模板實(shí)例化之前,完全沒有辦法來區(qū)分它們,這絕對(duì)是滋生各種 bug 的溫床。這時(shí) C++標(biāo)準(zhǔn)委員會(huì)再也忍不住了,與其到實(shí)例化時(shí)才能知道到底選擇哪種方式來解釋以上代碼,委員會(huì)決定引入一個(gè)新的關(guān)鍵字,這就是typename。

千呼萬喚始出來,我們來看看 C++標(biāo)準(zhǔn):

對(duì)于用于模板定義的依賴于模板參數(shù)的名稱,只有在實(shí)例化的參數(shù)中存在這個(gè)類型名,或者這個(gè)名稱前使用了typename關(guān)鍵字來修飾,編譯器才會(huì)將該名稱當(dāng)成是類型。除了以上這兩種情況,絕不會(huì)被當(dāng)成是類型。

因此,如果你想直接告訴編譯器T::const_iterator是類型而不是變量,只需用typename修飾:

typenameT::const_iteratorit(proto.begin());

這樣編譯器就可以確定T::const_iterator是一個(gè)類型,而不再需要等到實(shí)例化時(shí)期才能確定,因此消除了前面提到的歧義。

嵌套從屬類型

事實(shí)上類型T::const_iterator依賴于模板參數(shù)T, 模板中依賴于模板參數(shù)的名稱稱為從屬名稱(dependent name), 當(dāng)一個(gè)從屬名稱嵌套在一個(gè)類里面時(shí),稱為嵌套從屬名稱(nested dependent name)。 其實(shí)T::const_iterator還是一個(gè)嵌套從屬類型名稱(nested dependent type name)。

嵌套從屬名稱是需要用typename聲明的,其他的名稱是不可以用typename聲明的。比如下面是一個(gè)合法的聲明:

templatevoidfun(constT&proto,typenameT::const_iteratorit);

使用

在定義類模板或者函數(shù)模板時(shí),typenameclass關(guān)鍵字都可以用于指定模板參數(shù)中的類型。也就是說,以下兩種用法是完全等價(jià)的。

template/*... */;template    /* ... */;

既然typename關(guān)鍵字已經(jīng)存在,而且它也可以用于最常見的指定模板參數(shù),那么為什么不廢除class這一用法呢?答案其實(shí)也很明顯,因?yàn)樵谧罱K的標(biāo)準(zhǔn)出來之前,所有已存在的書、文章、教學(xué)、代碼中都是使用的是class,可以想像,如果標(biāo)準(zhǔn)不再支持class,會(huì)出現(xiàn)什么情況。

使用關(guān)鍵字typename代替關(guān)鍵字class指定模板類型形參更為直觀,畢竟,可以使用內(nèi)置類型(非類類型)作為實(shí)際的類型形參,而且,typename更清楚地指明后面的名字是一個(gè)類型名。但是,關(guān)鍵字typename是作為標(biāo)準(zhǔn) C++的組成部分加入到 C++中的,因此舊的程序更有可能只用關(guān)鍵字class。

這就是我分享的 c++的 typename,此外如果大家有什么更好的思路,也歡迎分享交流哈。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

一個(gè)程序員,喜歡寫文章,還喜歡打籃球,也喜歡吉他鋼琴的駁雜之人。日常更新自己,分享包括但不限于C/C++、嵌入式、物聯(lián)網(wǎng)、Linux等編程學(xué)習(xí)筆記,同時(shí),公眾號(hào)內(nèi)包含大量的學(xué)習(xí)資源。歡迎關(guān)注,一同交流學(xué)習(xí),共同進(jìn)步!