[笔记No.1]C++模板攻略-函数模板

前言

《C++ Templates Complete Guide 2nd Edition》涵盖了C++11,C++14和C++17的所有内容。如果你的C++编程修为想更进一步的话,那么这本书是一本非常好的C++编程进阶书籍,阅读这本书的必须具备了C++中等程度以后的基础,读这本书没什么压力。虽然大部分写C++模板技术的技术文章,没有显式地说明,但我认为专攻C++方向,这是学习路线图中一个比较正确的顺序(要点不全).请确保你深入学习模板之前,最起码要确保有如下C++基础。

  • C++类型系统:熟悉数据类型与转换机制。
  • C++STD基础的常用的容器和函数API有所了解。
  • C++面向对象(对类接口,类定义,继承与多态有所了解)
  • 熟悉C指针与内存管理有所了解。

我一直深受一句话的影响:“C的灵魂是指针,而后继者C++的核心技术就是模板”,因为C++ 标准库本身各种类库和函数API就大规模地使用了模板技术。

这本书绝对是一本良心的书籍,因为从第一版到第二版作者一直坚持不懈地跟随C++新版的步伐,扩充了有关模板技术的新内容。网上那些所谓的阿猫啊狗“原创”的C++模板技术培训班用到教学素材要么来自这本书的第一版或者第二版。因此,我认为要完全透彻掌握C++模板技术,一定要看这本书的英文原版。

  • 一来是从精神上支持该书的原作者,不吝啬那么一点零花钱的话可以购买该书授权副本。
  • 二来因为有关该书中的一些专业术语(英文),这本书如果解答得不全的,你可以stackflow或者去google会找到更多模板技术的相关内容,我认为发散性地去慢慢品读这本书。

如果你的C++编程技能偏向于为其他高层语言例如Python/Cython/Java等写更底层类库,函数API和专攻算法实现,那么这本书是必读的。就我目前的程度而言,要实现用C++现实各种常用的算法,我认为完成Part 1的模板知识已经够用了,如果需要升华到融合目前主流的设计模式并设计出自己独有的编程模式的,那么可能要将这本书啃完。这本书分三卷《Part1》,《Part2》和《Part3》。我前期的读书随笔,仅会完成Part1。


封面

罗嗦的话说到这里,我们进入正题

攻略要点

基本术语入门,按图说话,正入下文。

下面的函数模板定义了一个关于max的函数族,其中T是模板的模板参数(Template Parameter)可以表示一个具体的数据类型,例如int,double,std::string等等...

template<typename T>
T max(T a, T b){return b<a?a:b;}

其中的typename关键字来定义模板参数,也可以使用class关键字替换

调用示例

int main()
{
    auto r1=::max(2.3,44.44);
    auto r2=::max(77.23,52);
    auto r3=::max("Hello-World!!","IT-dog");

    
    std::cout<<"r1:"<<r1<<std::endl;
    std::cout<<"r2:"<<r2<<std::endl;
    std::cout<<"r3:"<<r3<<std::endl;
    return 0;
}

上面的调用示例,max函数模板没有被编译成可以处理任何类型的单一实体。 相反,对于使用该模板的每种类型,都会从模板生成不同的实体。 也就是编译后会有三个关于max函数的重载版本。因此,将针对这三种类型中的每种类型编译max()。 例如,

返回r1对应的max函数体

double max(double a,double b);

返回r2对应的函数体是

double max(double a,int b);

返回r3对应的max函数签名

std::string max(std::string a,std::string b);

那么上面关于函数max的重载集中,不同的重载版本的参数类型,C++的模板系统是如下推导的呢?

模板参数的推导过程

当我们为某些参数调用函数模板(例如max())时,模板参数的最终类型由我们传递的参数的类型确定的。 如果我们将两个int传递给参数类型T,则C ++编译器就得知T必须为int。

请注意,在类型推导过程中,自动类型转换受到限制:

  • 通过引用声明调用参数时,即使是trivial的转换也不适用于类型推导。 使用相同模板参数T声明的两个参数必须完全匹配

  • 当按值声明调用参数时,仅支持微妙的退化转换:const或volatile的限定将被忽略,引用转换为引用的类型,原始数组函数转换为相应的指针类型。 对于使用相同模板参数T声明的两个参数,退化类型必须匹配。
    例如

template<typename T>
T max(T a,T b){
    return a>b?a:b;
}

int c=72;
const int b=42;
max(b,c);   //T会被推导为int
max(c,c);  //T会被推导为int

int i=74;   
int &r=i;  
max(i,b);  //T会被推导为 int类型

int arr[3];
foo(&r,arr); //T会被推导为int*

然而下面向模板参数传递的第一个参数是int类型,第二个参数是double类型,但max函数模板的模板参数返回类型只定义了T,C++编译器要么匹配int max(int,int),要么匹配double max(double,double)

max(4,7.2);

避免这种编译时的的错误,我们在调用代码中会最好显式传入模板参数

max<double>(4,7.2);

max(static_cast<double>(4),7.2)

这样C++编译器可以推导为 模板参数T为double,但后一种,我个人是不太喜欢这样书写代码的。

默认参数的类型推导

类型推导不使用与默认调用参数

template<typename T>
void foo(T=""){}

int main()
{
    foo();
    return 0;
}

2019-12-27 19-18-14屏幕截图.png

为了支持这种情况,我们必须为模板参数声明一个默认的参数类型,

template<typename T=std::string>
void foo(T=""){}

int main()
{
    foo();
    return 0;
}

下面的示例,C++编译器能够推导的模板参数T为std::string类型,即生成的void foo(std::string)

多个模板参数

上面的max模板参数,我们可以为多个不同模板参数,下面的例子,我们我们在template的模板参数列表,定义了两个模板参数,分别是T1,T2表明它们告知编译器可以匹配两个不同类型的参数,并且max函数模板的返回类型为T1,表明匹配时,返回类型取决于在匹配时第一个模板参数推导的类型

template<typename T1,typename T2>
T1 max(T1 x,T2 y){
      return x>=y?x:y;
}

auto r1=::max(4,7.2);
auto r2=::max(24.5,8);

上面例子中r1对应的max函数重载版本为int max(int,double),因为在调用函数中,向max的模板函数传入的第一个参数是4,是一个int类型,而该类型决定了模板函数的返回的类型是int。

同理,r2对应的max函数重载版本为double max(double,int);

有时这样的情况,不是我们所期望的。因此有集中不同的解决方法。

1.3.1返回类型的模板参数

  • 可以在模板参数队列里额外指定第三个模板参数,但你必须在调用者函数中要显式指定返回的类型,例如下面的例子,显式告知编译器返回的类型是double
template<typename T1,typename T2,typename RT>
RT max(T1 a,T1 b){ ....}

int main(void){
    ::max<int,double,double>(44,63.2);
}

我个人不喜欢这种方式,可以再次改进一下上面的例子

template<typename RT,typename T1,typename T2>
RT max(T1 a, T2 b){
      return a>b?a:b;
}

int main(void){
      ::max<double>(44,63.2);
}

上面的例子,仅显式指定返回类型为double,剩下的模板参数由传入参数的类型来推导。

1.3.2推导返回类型

  • 从C++14开始,模板函数的返回类型可以通过auto关键字,告知编译器在编译时推导。
#include <type_trains>

template<typename T1,typename T2>
auto max(T1 a,T2 b)-> typename std::decay<decltype(a>b?a:b)>::type{
    return a>b?a:b;
}
int main(void){
    auto r1=::max(4,7.2);
    auto r2=::max(22.1,8.3);
    
    std::cout<<"r1:"<<r1<<std::endl;
    std::cout<<"r2:"<<r2<<std::endl;
}

实际上,对返回类型使用auto而不使用相应的尾随返回类型(将在末尾添加->)表
示必须从函数体内的return语句推导出实际的返回类型.这是使用类型特征std::decay<>,它以成员类返回结果类型。由于成员类也是一种类型,因此它必须用typename限定表达式才能访问。

1.3.3 返回值是一般类型

这是C++11之后,标准哭提供的一种方式std::common_type_t<>

#include <type_trains>

template<typename T1,typename T2>
std::common_type_t<T1,T2> max(T1 a,T2 b){
    return a>b?a:b;
}

首先,我个人是不喜欢这种方式,为何我简单方便的auto关键字不用,我还要敲打那么多代码~!,而且作者也认为这种方式对后期维护会带来一些麻烦的?因此我这里就不往下说了。请查看原书的48页的内容。

1.4 默认模板参数

(略)我个人同样的返回值推导复杂化,没必要使用(原书49-51页的内容)。

重载模板函数

后面会继续更新....

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