C++模板類型推導

模板是C++的重要特性枚抵,是C++標準模板庫的基礎。模板可以根據(jù)數(shù)據(jù)類型自動生成代碼,大大減少重復代碼退盯。模板實例化的時候編譯器需要根據(jù)具體變量推導數(shù)據(jù)類型,模板推導出的類型很多時候是顯而易見的泻肯,有些時候卻不太明顯渊迁,本文詳細闡述一下C++模板的類型推導機制。

在C++中聲明一個模板函數(shù)的偽代碼如下:

template<typename T>
void f(ParamType param);

上面的ParamTypeT不一定相同灶挟,比如:

template<typename T>
void f(const T& param);

此時ParamType的類型是const T&琉朽。

調(diào)用模板函數(shù)方式如下:

f(expr);

編譯的時候,編譯器通過表達式expr推導出兩個類型ParamTypeT稚铣。

模板的類型推導與ParamType密切相關(guān)箱叁,根據(jù)ParamType的類型可以分為三種情形:

  1. ParamType 既不是指針也不是引用。
  2. ParamType 是指針或引用惕医,不是通用引用耕漱。
  3. ParamType 是通用引用。

下面分別討論一下三種情形:

ParamType 既不是指針也不是引用

ParamTypeT相同抬伺,此時模板如下:

template<typename T>
void f(T param);

這種情況下參數(shù)param的類型T可以理解為值傳遞param會復制一份expr)時的類型螟够。這意味著:

  • 如果expr是一個引用,忽略引用部分峡钓。
  • 如果expr帶有constvolatile妓笙,忽略constvolatile能岩。

舉個例子:

int x = 27;
const int cx = x;
const int& rx = x;
f(x);
f(cx);
f(rx);

上面的三種調(diào)用方式寞宫,T的類型都是int

需要注意下面一種情況:

const char* const ptr = "Fun with pointers";
f(ptr);

ptr是指向字符串常量的常量指針拉鹃,此時進行值傳遞T的類型應為const char*辈赋。因為:

  1. 開頭的const修飾的是指向的對象鲫忍,表示指向的字符串不可變,不可忽略钥屈,否則指向的類型就不對了饲窿。
  2. *右邊的const修飾的是指針,表示指針本身不可變焕蹄,在值傳遞的情況下指針是被復制一份,該const沒有意義阀溶。

ParamType 是指針或引用腻脏,不是通用引用

這種情況下,推導步驟如下:

  1. 如果expr是一個引用银锻,忽略引用部分永品。
  2. expr類型與ParamType進行模式匹配,先確定ParamType击纬,再根據(jù)ParamType推導T鼎姐。

舉個例子:

template<typename T>
void f(T& param);  // 參數(shù)是引用類型

int x = 27;
const int cx = x;
const int& rx = x;

f(x);  // x是int類型,ParamType類型是int&更振,所以T是int類型
f(cx); // cx是const int類型炕桨,ParamType類型是const int&,所以T是const int類型
f(rx); // rx是const int&類型肯腕,忽略引用部分献宫,同cx,ParamType類型是const int&实撒,所以T是const int類型

如果把參數(shù)類型改為const T&姊途,相應的T的類型也會有一點變化:

template<typename T>
void f(const T& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x);  // x是int類型,ParamType類型是const int&知态,所以T是int類型
f(cx); // cx是const int類型捷兰,ParamType類型是const int&,所以T是int類型
f(rx); // rx是const int&類型负敏,忽略引用部分贡茅,同cx,ParamType類型是const int&其做,所以T是int類型

如果參數(shù)是指針類型友扰,也類似:

template<typename T>
void f(T* param);  // 參數(shù)是指針類型

int x = 27;
const int *px = &x;

f(&x); // &x是int*類型,ParamType類型是int*庶柿,所以T是int類型
f(px); // px是const int*類型村怪,ParamType類型是const int*,所以T是const int類型

ParamType 是通用引用

這種情況下浮庐,推導規(guī)則如下:

  • 如果expr是左值甚负,那么TParamType都是左值引用柬焕。這條規(guī)則很特殊:首先,這是唯一一種T被推導為引用類型的情形梭域;其次斑举,ParamType的聲明形式是右值引用的語法,但是實際類型為左值引用病涨。
  • 如果expr是右值富玷,推導規(guī)則與普通引用一致。

舉例:

template<typename T>
void f(T&& param); // 參數(shù)是通用引用

int x = 27;
const int cx = x;
const int& rx = x;

f(x);  // x 是左值既穆,所以T和ParamType都是int&類型
f(cx); // cx 是左值赎懦,所以T和ParamType都是const int&類型
f(rx); // rx 是左值,所以T和ParamType都是const int&類型
f(27); // 27 是右值幻工,ParamType是int&&類型励两,所以T是int類型

以上三種情形就是C++模板類型推導的全部規(guī)則了。下面簡單補充說明一下C++里兩種特殊的參數(shù)類型:數(shù)組參數(shù)和函數(shù)參數(shù)囊颅。

數(shù)組參數(shù)

在C語言和C++里当悔,如下的兩個函數(shù)聲明是完全等價的:

void myFunc(int param[]);
void myFunc(int* param);

即函數(shù)的數(shù)組參數(shù)會被當成指針參數(shù)

如果要使用真正的數(shù)組參數(shù)踢代,在C++中可以使用數(shù)組引用類型:

void myFunc(int (&param)[5]); // 使用數(shù)組引用時必須指定數(shù)組大小盲憎,同定義數(shù)組一樣

int (&param)[5]表示一個大小為5的整數(shù)數(shù)組引用類型。

了解數(shù)組參數(shù)的以上特點后胳挎,可以很容易的理解模板如何推導數(shù)組類型的參數(shù)焙畔。舉兩個例子:

template<typename T>
void f(T param);

const char name[] = "J. P. Briggs";
f(name); // name是數(shù)組,param是值傳遞, 數(shù)組參數(shù)當成指針參數(shù)串远,因此param的類型是const char*
template<typename T>
void f(T& param);

const char name[] = "J. P. Briggs";
f(name); // name是數(shù)組宏多,param是引用類型,因此param的類型是const char(&)[13]

函數(shù)參數(shù)

除了數(shù)組參數(shù)澡罚,函數(shù)參數(shù)也會被當成函數(shù)指針伸但。函數(shù)參數(shù)的類型推導規(guī)則跟數(shù)組參數(shù)完全一樣。

void someFunc(int, double);

template<typename T>
void f1(T param);

template<typename T>
void f2(T& param);

f1(someFunc); // param是值傳遞, 函數(shù)參數(shù)當成函數(shù)指針留搔,因此param的類型是void (*)(int, double)
f2(someFunc); // param是引用類型更胖,因此param的類型是void (&)(int, double)

實際使用中函數(shù)指針和函數(shù)引用基本沒有區(qū)別。兩者調(diào)用時都可以解引用或直接調(diào)用隔显,唯一的區(qū)別是引用初始化時只能用函數(shù)名稱却妨,不能在前面加&。

void (*pf)(int) = someFunc; // 也可以寫成 void (*pf)(int) = &someFunc;
void (&rf)(int) = someFunc;

pf(8, 1.2); // 也可以寫成 (*pf)(8, 1.2);
rf(8, 1.2); // 也可以寫成 (*rf)(8, 1.2);

總結(jié):

  • ParamType 既不是指針也不是引用時括眠,采用值傳遞模式彪标,忽略表達式的引用部分、const掷豺、volatile捞烟。
  • ParamType 是指針或引用薄声,不是通用引用時,忽略引用部分题画,進行模式匹配默辨,先確定ParamType,再推導T苍息。
  • ParamType 是通用引用時缩幸,左值特殊對待(T和ParamType都是左值引用)。
  • 數(shù)組參數(shù)竞思、函數(shù)參數(shù)非引用傳遞時當作指針表谊,引用類型不會。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末衙四,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子患亿,更是在濱河造成了極大的恐慌传蹈,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件步藕,死亡現(xiàn)場離奇詭異惦界,居然都是意外死亡,警方通過查閱死者的電腦和手機咙冗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門沾歪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人雾消,你說我怎么就攤上這事灾搏。” “怎么了立润?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵狂窑,是天一觀的道長。 經(jīng)常有香客問我桑腮,道長泉哈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任破讨,我火速辦了婚禮丛晦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘提陶。我一直安慰自己烫沙,他們只是感情好夭谤,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布挟秤。 她就那樣靜靜地躺著篮奄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旁振。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天嗜闻,我揣著相機與錄音斩芭,去河邊找鬼。 笑死蝶糯,一個胖子當著我的面吹牛洋只,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昼捍,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼识虚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了妒茬?” 一聲冷哼從身側(cè)響起担锤,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乍钻,沒想到半個月后肛循,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡银择,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年多糠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浩考。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡夹孔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出析孽,到底是詐尸還是另有隱情搭伤,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布袜瞬,位于F島的核電站闷畸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吞滞。R本人自食惡果不足惜佑菩,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望裁赠。 院中可真熱鬧殿漠,春花似錦、人聲如沸佩捞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽一忱。三九已至莲蜘,卻和暖如春谭确,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背票渠。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工逐哈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人问顷。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓昂秃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杜窄。 傳聞我的和親對象是個殘疾皇子肠骆,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 心血來朝的就想翻譯一下Effective Modern C++,非嚴謹翻譯塞耕,大伙兒湊合著看吧蚀腿。記得剛接觸C++是考...
    EVANMORE閱讀 786評論 0 0
  • C++類型推斷 對于靜態(tài)語言來說,你一般要明確告訴編譯器變量或者表達式的類型扫外。但是慶幸地是莉钙,現(xiàn)在C++已經(jīng)引入了自...
    小白將閱讀 2,484評論 1 10
  • 模板和調(diào)用的一般形式: 從expr來推導T 和ParamType的類型 情況1:ParamType是個指針或引用,...
    rmrfany閱讀 326評論 1 2
  • 從函數(shù)模板談起 函數(shù)模板的類型推導機制是在C++98時代就有的畏浆,auto的類型推導機制與其基本一致胆胰,所以先理解函數(shù)...
    leon4ever閱讀 927評論 0 0
  • Java5.0 在java.util.concurrent 包中提供了多種并發(fā)容器類來改進同步容器的性能狞贱。 Con...
    匆匆歲月閱讀 303評論 0 0