1.模板观念与函数模板

项目地址

课程内容

  • Part1 C++模板简介(An Introduction to C++ Template)
  • Part2 泛型编程(Generic Programming)
  • Part3 容器(Containers)
  • Part4 一些进阶问题(Some Advanced Topic)

Part1 C++模板简介

  • C++模板概观(Overview)
  • C++函数模板(Function Template)
  • C++类模板(Class Template)
  • C++操作符重载(Operator Overloading)

C++模板概观(1)

  • 模板(Templates)是C++的一种特性,允许函数或类(对象)通过泛型(generic types)的形式表现或运行
  • 模板可以使得函数或类在对应不同的型别(types)的时候正常工作,而无需为每一个型别都写一份代码
  • 一个简单的例子:
  • 如果要写一个取两个数中较大值的函数Max,在不使用模板的情况下,我们不得不针对不同的型别(比如int,long,char)提供每一种型别的重载:

int Max(int a, int b)
{
    return (a>b)?a:b;
}

long Max(long a, long b)
{
    return (a>b)?a:b;
}

char Max(char a, char b)
{
    return (a>b)?a:b;
}

C++模板概观(2)

  • 一个简单的例子(续)
  • 如果使用模板,则可以省去一堆亢余代码,从而将函数原型缩减到非常简介的表达:

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

  • C++主要有两种类型的模板:
  • 类模版(Class template):使用泛型参数的类(classes with generic parameters)
  • 函数模板(Function template):使用泛型参数的函数(functions with generic parameters)

C++模板概观(3)

  • 模板实例化
  • 模板的声明(declaration)其实并未给出一个函数或类的完全定义(definition),只是提供了一个函数或类的语法框架(syntactical skeletion)
  • 实例化是指从模板构建出一个真正的函数或类的过程,比如
    template <typename T> struct Object {...};
    可以用来构建诸如Object<int>,Object<char>,Object<int>,Object<MyClass>等等不同型别的具体事例
  • 实例化有两种类型:
  • 显式实例化-在代码中明确指定要针对哪种型别进行实例化
  • 隐式实例化-在首次使用时根据具体情况使用一种合适的型别进行实例化

C++函数模板(1)

  • 什么是函数模板?
  • 函数模板是参数化的一族函数(a family of function)
  • 通过函数模板,可以定义一系列函数,这些函数都基于同一套代码,但是可以作用在不同型别的参数上

template <typename T>
inline T Max(
    const T& a,
    const T& b)
{
    return (a > b) ? a:b;
}

  • 定义函数模板
  • 定义一个函数模板,返回两数中较大的那个,该函数有两个参数:(a,b)
  • 参数型别未定,以模板参数T表示
  • 模板参数由关键typename引入

C++函数模板(2)

  • 定义函数模板(续)
  • 也可以使用class替代typename来定义型别参数
    template <class T> inline T Max(const T& a, const T& b) {
    ...}
  • 从语法上讲使用class和使用typename没有区别
  • 但从语义上,class可能会导致误区,即只有类才能作为型别参数;而事实上T所表达的意思不仅仅只针对类,任何型别都可以
  • 请尽量使用typename!
  • class可以取代typename,但struct却不可以,以下写法语法上是错误的:
    //this is wrong!!!
    template <struct T> inline T Max(const T& a, const T& b)
    {
    ...
    }

C++函数模板(3)

  • 模板函数的使用
  • 调用Max,用int,float,以及std::wstring作为模板参数替换T
  • 对于不同的型别,都从模板实例化出不同的函数实体
  • 但是不可以使用不同型别的参数来调用Max,因为编译器在编译时已经知道Max函数需要传递的型别

int i = 7, j = 30;
_tprintf(TEXT("Max(i,j) = %d\n"), Max(i,j));

double f = -1.8, g = -0.9;
_tprintf(TEXT("Max(f,g) = %f\n"), Max(f,g));

std::wstring s1 = TEXT("mathematics"), s2 = TEXT("math");
_tprintf(TEXT("Max(s1,s2) = %s\n"), Max(s1,s2).c_str());

Max(i,f);//compile error: template parameter 'T' is ambiguous  

C++函数模板(4)

  • 模板实例化
  • 用具体型别替代模板参数T的过程叫做实例化(instantiation);从而产生了一个模板事例
  • 一旦使用函数模板,这种实例化过程便由编译器自动触发的,不需要额外去请求模板实例化
  • 如果实例化一种型别,而该型别内部并不支持函数所使用的操作,那么就会导致一个编译错误,如下所示:
  • std::complex并没有重载“>”,也就是说改型别并不支持使用“>”比较大小,而Max函数使用“>”来判断c1,c2的大小,所以无法通过Max(c1,c2)得到预期的结果
    std::complex<int> c1(1,2), c2(15,16); //编译错误!

C++函数模板(5)

  • 结论:模板被编译了两次
  1. 没有实例化之前,检查模板代码本身是否有语法错误
  2. 实例化期间,检查对模板代码的调用是否合法

C++函数模板(6)

  • 参数推导

  • 模板参数是有传递给模板函数的实参决定的

  • 不允许自动型别转换:每个T必须严格匹配!
    Max(1,2) //OK:两个实参的型别都是int
    Max(1,2.0) //ERROR:第一个参数型别是int,第二个参数型别是double

    • 一般有两种处理这种错误的方法:
      1. 用static_cast或强制转换参数型别以使两者匹配
        Max(static_cast<double>(1), 2.0)
      2. 显式指定T的型别
        Max<double>(1,2.0)

C++函数模板(7)

  • 函数模板重载
  • 函数模板也可以像普通函数一样被重载
  • 非模板函数可以和同名的模板函数共存
  • 编译器通过函数模板参数推导来决定使用调用哪个重载

//普通函数
//1
inline int const& Max(const int const& a, const int const& b)

//2
template <typename T>
inline T const& Max(const T const& a, const T const& b)

//3
template <typename T>
inline T const& Max(const T const& a, const T const& b, const T const& c)

C++函数模板(8)

函数模板重载(续)

  • Max(7, 42, 68):调用接受三个参数的模板 ————3
  • Max(7.0, 42.0):调用Max<double> (参数推导) ————2
  • Max('a', 'b'):调用Max<char> (参数推导) ————2
  • Max(7, 42):调用非模板函数,参数型别为int ————1 其他因素都相同的情况下,重载裁决过程调用非模板函数,而不是从模板产生实例
  • Max<>(7, 42):调用Max<int> (参数推导) ————2 允许空模板参数列表
  • Max<double>(7, 42): 调用Max<double> (无需参数推导) ————2
  • Max('a', 42.7):调用非模版函数,参数型别为int ————1 对于型别不同的 参数只能调用非模版函数(char型别'a'和double型别42.7都将转化为int型别)

C++函数模板(9)

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

推荐阅读更多精彩内容