binarytreenode”使用 类 模板 需要 模板 参数列表_c++1117 模板核心知识(一)—— 函数模板...
目錄
- 定義函數(shù)模板
- 使用函數(shù)模板
- 兩階段翻譯 Two-Phase Translation
- 模板的編譯和鏈接問題
- 多模板參數(shù)
- 引入額外模板參數(shù)作為返回值類型
- 讓編譯器自己找出返回值類型
- 將返回值聲明為兩個(gè)模板參數(shù)的公共類型
- 默認(rèn)模板參數(shù)
- 重載函數(shù)模板
- 重載時(shí)最好不要隨便改變模板參數(shù)個(gè)數(shù),最好可以顯示的指定模板參數(shù)類型
- 確保所有被重載的函數(shù)模板在使用時(shí)已經(jīng)被聲明定義
定義函數(shù)模板
template<typename?T>T?max(T?a,T?b)?{
??return?b?}
使用函數(shù)模板
??std::cout?<7,42)?<std::endl;??std::cout?<1.1,2.2)?<std::endl;
??std::cout?<"math","mathematics")?<std::endl;
模板不是被編譯成可以處理任何類型的單個(gè)函數(shù)。相反,編譯器會(huì)針對(duì)每一個(gè)使用該模板的類型生成對(duì)應(yīng)的函數(shù)。例如,max(7,42)的調(diào)用在語義上相當(dāng)于調(diào)用了:
int?max(int?a,int?b)?{??return?b?}
double、string 同理。
將模板參數(shù)替換成具體參數(shù)類型的過程叫做instantiation,這個(gè)過程會(huì)產(chǎn)生一個(gè)instance of template。
兩階段翻譯 Two-Phase Translation
如果某一特定參數(shù)類型不支持模板內(nèi)的操作,那么編譯階段會(huì)報(bào)錯(cuò),例如:
std::complex<float>?c1,c2;????????//不支持?max中的?...
max(c1,c2);
模板會(huì)分成兩個(gè)階段進(jìn)行”編譯“:
- 語法錯(cuò)誤,包括缺失分號(hào)。
- 使用未定義參數(shù)。
- 如果 static assertion 不依賴模板參數(shù),會(huì)檢查是否通過 static assertion.
例如:
template<typename?T>void?foo(T?t)?{??undeclared();?????????//?first-phase?compile-time?error?if?undeclared()?unknown
??undeclared(t);???????//?second-phase?compile-time?error?if?undeclared(T)?unknown
??static_assert(sizeof(int)?>?10,"int?too?small");??????//?first-phase?compile-time?error
??static_assert(sizeof(T)?>?10,?"T?too?small");????????//?second-phase?compile-time?error
}
模板的編譯和鏈接問題
大多數(shù)人會(huì)按照如下方式組織非模板代碼:
- 將類或者其他類型聲明放在頭文件(.hpp、.H、.h、.hh、.hxx)中。
- 將函數(shù)定義等放到一個(gè)單獨(dú)的編譯單元文件中(.cpp、.C、.c、.cc、.cxx)。
但是這種組織方式在包含模板的代碼中卻行不通,例如:頭文件:
//?myfirst.hpp#ifndef?MYFIRST_HPP
#define?MYFIRST_HPP
//?declaration?of?template
template<typename?T>void?printTypeof?(T?const&);
#endif?//?MYFIRST_HPP
定義函數(shù)模板的文件:
//?myfirst.cpp#include?
#include?
#include?"myfirst.hpp"
//?implementation/definition?of?template
template<typename?T>void?printTypeof?(T?const&?x)?{
??std::cout?<typeid(x).name()?<'\n';
}
在另一個(gè)文件中使用該模板:
//?myfirstmain.cpp#include?"myfirst.hpp"
//?use?of?the?template
int?main()?{
??double?ice?=?3.0;
??printTypeof(ice);?//?call?function?template?for?type?double
}
在 c/c++中,當(dāng)編譯階段發(fā)現(xiàn)一個(gè)符號(hào)(printTypeof)沒有定義只有聲明時(shí),編譯器會(huì)假設(shè)它的定義在其他文件中,所以編譯器會(huì)留一個(gè)”坑“給鏈接器 linker,讓它去填充真正的符號(hào)地址。
但是上面說過,模板是比較特殊的,需要在編譯階段進(jìn)行instantiation,即需要進(jìn)行模板參數(shù)類型推斷,實(shí)例化模板,當(dāng)然也就需要知道函數(shù)的定義。但是由于上面兩個(gè) cpp 文件都是單獨(dú)的編譯單元文件,所以當(dāng)編譯器編譯myfirstmain.cpp時(shí),它沒有找到模板的定義,自然也就沒有instantiation。
解決辦法就是我們把模板的聲明和定義都放在一個(gè)頭文件。大家可以看一下自己環(huán)境下的 vector 等 STL 源文件,就是把類的聲明和定義都放在了一個(gè)文件中。
多模板參數(shù)
template<typename?T1,?typename?T2>T1?max?(T1?a,?T2?b)?{
????return?b?}
...
auto?m?=?max(4,?7.2);???????//?注意:返回類型是第一個(gè)模板參數(shù)T1 的類型
但是問題正如注釋中說的,max 的返回值類型總是 T1。如果我們調(diào)用max(42, 66.66),返回值則是 66。
一般有三個(gè)方法解決這個(gè)問題:
- 引入額外模板參數(shù)作為返回值類型
- 讓編譯器自己找出返回值類型
- 將返回值聲明為兩個(gè)模板參數(shù)的公共類型,比如 int 和 float,公共類型就是 float
引入額外模板參數(shù)作為返回值類型
在函數(shù)模板的參數(shù)類型推導(dǎo)過程中,一般我們不用顯式指定模板參數(shù)類型。但是當(dāng)模板參數(shù)不能根據(jù)傳遞的參數(shù)推導(dǎo)出來時(shí),我們就需要顯式的指定模板參數(shù)類型。
template<typename?T1,?typename?T2,?typename?RT>RT?max(T1?a,?T2?b);
RT 是不能根據(jù)函數(shù)的參數(shù)列表推導(dǎo)出來的,所以我們需要顯式的指定:
max<int,?double,?double>(4,?7.2);或者我們改變模板參數(shù)列表順序,這種情況只需顯式的指定一個(gè)參數(shù)類型即可:
template<typename?RT?typename?T1,?typename?T2>??????//RT變?yōu)榈谝粋€(gè)模板參數(shù)RT?max(T1?a,?T2?b);
...
max<double>(4,?7.2);
讓編譯器自己找出返回值類型
在 C++11 中,我們可以利用 auto 和 trailing return type 來讓編譯器找出返回值類型:
template?<typename?T1,?typename?T2>auto?max(T1?a,?T2?b)?->?decltype(b??{return?b?}
decltype 后面的文章會(huì)講到,這里只需知道它可以獲取到表達(dá)式的類型。
我們可以寫的更簡(jiǎn)單點(diǎn):
template?<typename?T1,?typename?T2>auto?max(T1?a,?T2?b)?->?decltype(true???a?:?b)?{??????//?true???a?:?b
??return?b?}
關(guān)于?:返回值規(guī)則可以參考這個(gè):[Conditional Operator: ? :]()
看到true ? a : b不要奇怪為什么是 true,這里的重點(diǎn)不是計(jì)算返回值,而是得到返回值類型。
在 C++14 中,我們可以省略 trailing return type:
template<typename?T1,?typename?T2>auto?max?(T1?a,?T2?b)?{????return?b?}
將返回值聲明為兩個(gè)模板參數(shù)的公共類型
c++11 新特性std::common_type可以產(chǎn)生幾個(gè)不同類型的共同類型,其實(shí)核心意思跟上面說的差不多:
template?<typename?T1,?typename?T2>typename?std::common_type::type?max(T1?a,?T2?b)?{return?b?}
在 c++14 中,可以更簡(jiǎn)單的寫:
template?<typename?T1,?typename?T2>std::common_type_t?max(T1?a,?T2?b)?{
??return?b?}
這里使用_t后綴讓我們不用寫typename和::type。類似的還有_v,這個(gè)在 c++14 的type traits里很常見。
默認(rèn)模板參數(shù)
這個(gè)很像函數(shù)的默認(rèn)參數(shù),直接看例子:
template?<typename?T1,?typename?T2,?typename?RT?=?std::common_type_t>RT?max(T1?a,?T2?b)?{return?b?}auto?a?=?max(4,?7.2);auto?b?=?max<double,int,long?double>(7.2,?4);
正如第二個(gè)用法,如果我們想顯示的指明 RT 的類型,必須顯示的指出全部三個(gè)參數(shù)類型。但是與函數(shù)默認(rèn)參數(shù)不同的是,我們可以將默認(rèn)參數(shù)放到第一個(gè)位置:
template?<typename?RT?=?long,?typename?T1,?typename?T2>RT?max(T1?a,?T2?b)?{
??return?b?}
int?i;
long?l;
…
max(i,?l);?????????????????????//?返回值類型是long?(RT?的默認(rèn)值)
max<int>(4,?42);??????//返回int,因?yàn)槠浔伙@式指定
重載函數(shù)模板
這個(gè)跟普通函數(shù)重載也類似:
//?maximum?of?two?int?values:int?max(int?a,?int?b)?{
??return?b?}
//?maximum?of?two?values?of?any?type:
template?<typename?T>
T?max(T?a,?T?b)?{
??return?b?}
int?main()?{
??max(7,?42);?????????//?calls?the?nontemplate?for?two?ints
??max(7.0,?42.0);?????//?calls?max?(by?argument?deduction)
??max('a',?'b');??????//?calls?max?(by?argument?deduction)
??max<>(7,?42);???????//?calls?max?(by?argument?deduction)
??max<double>(7,?42);?//?calls?max?(no?argument?deduction)
??max('a',?42.7);?????//?calls?the?nontemplate?for?two?ints
}
這里需要解釋下最后一個(gè)max('a', 42.7)。因?yàn)樵谀0鍏?shù)類型推導(dǎo)過程中不允許類型自動(dòng)轉(zhuǎn)換,但是調(diào)用普通函數(shù)是允許的,所以這個(gè)會(huì)調(diào)用非模板函數(shù)。
ps. 由于函數(shù)模板重載,所以函數(shù)模板并不像類模板一樣可以進(jìn)行偏特化。
還有兩點(diǎn)關(guān)于重載的基本原則需要了解一下:
重載時(shí)最好不要隨便改變模板參數(shù)個(gè)數(shù),最好可以顯示的指定模板參數(shù)類型
下面是段有問題的代碼:
//?maximum?of?two?values?of?any?type?(call-by-reference)template?<typename?T>?T?const?&max(T?const?&a,?T?const?&b)?{
??return?b?}
//?maximum?of?two?C-strings?(call-by-value)
char?const?*max(char?const?*a,?char?const?*b)?{
??return?std::strcmp(b,?a)?0???a?:?b;
}
//?maximum?of?three?values?of?any?type?(call-by-reference)
template?<typename?T>?T?const?&max(T?const?&a,?T?const?&b,?T?const?&c)?{
??return?max(max(a,?b),?c);???????????//?error?if?max(a,b)?uses?call-by-value
}
int?main()?{
??auto?m1?=?max(7,?42,?68);?????????//?OK
??char?const?*s1?=?"frederic";
??char?const?*s2?=?"anica";
??char?const?*s3?=?"lucas";
??auto?m2?=?max(s1,?s2,?s3);?????????//?run-time?ERROR
}
問題出現(xiàn)在return max (max(a,b), c);,因?yàn)閏har const *max(char const *a, char const *b)的參數(shù)是按值傳遞,max(a,b)會(huì)產(chǎn)生一個(gè)指向已經(jīng)銷毀的棧幀地址,這會(huì)導(dǎo)致未定義行為。
確保所有被重載的函數(shù)模板在使用時(shí)已經(jīng)被聲明定義
//?maximum?of?two?values?of?any?type:template?<typename?T>
T?max(T?a,?T?b)?{
??std::cout?<"max()\n";return?b?}//?maximum?of?three?values?of?any?type:template?<typename?T>T?max(T?a,?T?b,?T?c)?{return?max(max(a,?b),?c);
}//?maximum?of?two?int?values:int?max(int?a,?int?b)?{std::cout?<"max(int,int)?\n";return?b?}int?main()?{
??max(47,?11,?33);????//?max()
}
這點(diǎn)很好理解。
(完)
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的binarytreenode”使用 类 模板 需要 模板 参数列表_c++1117 模板核心知识(一)—— 函数模板...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何用excel筛选相似内容_Excel
- 下一篇: c#获取本地ip地址网关子网掩码_教你如