c++ 理解模板的类型推导

说明

本文来自《Effective Modern C++》,自己做了个学习笔记,一是加深印象,二是方便后期自己查阅,再来也希望帮助到有需要的同学。泛型编程是c++重中之重,模板是c++泛型编程的一大块,理解模板的类型推导才能更好的掌握c++模板。

模板形式

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

在编译期,编译器会通过函数的实参分别推导T和ParamType的类型,ParamType相对于T,会额外加一些修饰词,如const或引用符号等限定词。

demo

  1. 模板函数声明
template<typename T>
void f(const T& param);
  1. 调用函数
int x = 0;
f(x);

这里T被编译器推导为int,而ParamType被推导为 const int&。如果我们没有深入的了解,可能就会认为实参类型是什么,T就会被推导为是什么类型,其实T的类型推导还要依赖ParamType

三种情况

接下来我们看一下类型推导的三种情况:

情况 1:ParamType是指针或者引用,而不是万能引用

这是最简单的情况,注意两点即可:

  1. 如果实参是引用类型,则先忽略引用部分。
  2. 之后,根据实参的类型和ParamType的修饰词来决定T的类型。

示例 1:
函数模板

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

变量

int x = 27;             // x 的类型是 int
const int cx = x;       // cx 的类型是const int
const int& rx = x;      // rx 的类型是const int 的引用

各次调用中,对param和T的推导结果如下:

f(x);        // T 的类型是 int, param 的类型是 int& 
f(cx);       // T 的类型是 const int, param 的类型是 const int&
f(rx);       // T 的类型是 const int,param 的类型是 const int&

解释:第一个是我们想当然的结果,很好理解。第二个和第三个调用中,由于函数形参加了引用修饰(T&),所以参数传递的时候,要保留实参的属性,也就是要保持实参对象的不可修改的属性。这也是为什么向持有 T& 类型的模板传入const对象是安全的:该对象的常量性(constness)会成为 T 的类型推导结果的组成部分

接下来我们将函数形参类型加上const 关键字,看下稍微的区别:因为形参已经被const 修饰,所以推导结果 T 也就不需要const了。
示例 2:
函数模板

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

变量

int x = 27;             // x 的类型是 int
const int cx = x;       // cx 的类型是const int
const int& rx = x;      // rx 的类型是const int 的引用

各次调用中,对param和T的推导结果如下:

f(x);        // T 的类型是 int, param 的类型是 int& 
f(cx);       // T 的类型是 int, param 的类型是 const int&
f(rx);       // T 的类型是 int,param 的类型是 const int&

指针和引用没有区别
示例 3:
函数模板

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

变量

int x = 27;             // x 的类型是 int
const int *px = &x;     // px 是指向x的指针,类型为 const int *

调用

f(&x);    // T 的类型是 int, param 的类型是 int *
f(px);    // T 的类型是 const int, param 的类型是 const int *

情况 2:ParamType 是万能引用

模板函数形参声明为 T&&(也就是右值引用语法)。两种不同情况:

  1. 如果实参是做是左值,T 和 Paramtype 都会被推导为左值引用。这里与我们所想大不一样:首先,这是在模板类型推导中,T被推导为引用类型行的唯一情况。其次,尽管声明时使用的是有值引用语法,但是推导结果却是左值引用。
  2. 如果实参是右值,那和情况1规则一样

例子
函数模板

template<typename T>
void f(T&& param);      // param 现在是万能引用

变量

int x = 27;             // x 的类型是 int
const int cx = x;       // cx 的类型是const int
const int& rx = x;      // rx 的类型是const int 的引用

调用

f(x);    // x 是左值,所以T 的类型是 int &, param 的类型也是 int &
f(cx);   // cx 是左值,所以T 的类型是 const int &, param 的类型也是 const  int &
f(rx);   // rx是左值,所以T 的类型是 const int &, param 的类型也是 const  int &
f(27);   // 27 是左值,所以T 的类型是 int, param 的类型也是 int &&(右值引用)

关键在于,万能引用形参类型的推导会依据实参是右值还是左值。这里传左值时,大家可能不理解为什么T也被推导为引用类型,其实只要简单理解成T&&是一种特殊关键字就行了,此时它不是右值引用,二是万能引用。

情况 3: ParamType 既非指针也非引用

这时指的是值传递:

  1. 实参的引用还是忽略。
  2. const、volatile也要忽略。

例子 1
函数模板

template<typename T>
void f(T param);      // param 现在是值传递

变量

int x = 27;             // x 的类型是 int
const int cx = x;       // cx 的类型是const int
const int& rx = x;      // rx 的类型是const int 的引用

调用

f(x);    // T 和 param的类型都是 int
f(cx);   // T 和 param的类型都是 int
f(rx);   // T 和 param的类型都是 int

这里比较好理解,因为值传递进行实例化新的副本。所以实参原有的属性不应该影响副本。

这里有有个稍微不好理解的例子 const char * 类型,当然char也可以是int 或者其他。
例子 2
函数模板

template<typename T>
void f(T param);      // param 现在是值传递

变量

const char * ptr = "ABC";   // ptr 指向一个常量对象

调用

f(ptr);    // T 和 param 都是const char *

解释:因为值传递针对的是实参与形参之前的关系,我们不希望改变其他变量也就是 ptr 所指向的常量。

难点 :数组参数

为什么说这块难一点,因为我们都只到c/c++数组在传参时都会退化为指针。但是下面的例子则不然。
例子 1 :值传递
函数模板

template<typename T>
void f(T param);      // param 现在是值传递

变量

int arr[7] = {};  // arr 长度为7的int类型数组

调用

f(arr);    // T 和 param 都是 int *

这里例子好理解
例子 2 :引用传递
函数模板

template<typename T>
void f(T& param);      // param 现在是值传递

变量

int arr[5] = {};  // arr 长度为7的int类型数组

调用

f(arr);    // T 是实际数组类型 int [5],  param 被推导为长度为7的int类型数组引用(也就是 int (&)[7] )
           // sizeof(arr) = 20;

是不是很有意思。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354