2021-08-12

40.1 函数、类模版
模版可分两类:函数模版,类模版。函数模板可被重载,而类模板不能被重载,也就是说允许存在两个同名的函数模板,还可以对它们进行实例化,使它们具有相同的参数类型。函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。

40.2 模板特化
对单一模板提供的一个特殊实例,使每种类型都具有相同的功能,将一个或多个模板参数绑定到特定的类型或值上。特化分为了两种,全特化和偏特化。

不能将特化和重载混为一谈,全特化和偏特化都没有引入一个全新的模板或者模板实例。它们只是对原来的泛型(或者非特化)模板中已经隐式声明的实例提供另一种定义。在概念上,这是一个相对比较重要的现象,也是特化区别于重载模板的关键之处。类模板不可被重载,可以通过特化来实现相似的效果,从而可透明地获得具有更高效率的代码。

  1.  全特化
    

全特化是模板的一个唯一特例,指定的模板实参列表必须和相应的模板参数列表一一对应。不能用一个非类型值来替换模板类型参数。但如果模板参数具有缺省模板实参,那么用来替换实参就是可选的。

函数模板全特化:必须为原函数模板的每个模板参数都提供实参,且使用关键字template后跟一个空尖括号对<>,表明将原模板的所有模板参数提供实参。

template

void fun(T a) {

cout<< “The main template fun():” << a << endl;

}

template <> //对int型特例化

void fun(int a) {

cout<< “Specialized template for int type:”<< a << endl;

}

int main(){

fun(‘a’);

fun(10);

fun(9.15);

return 0;

}

对于除int型外的其他数据类型,都会调用通用版本的函数模板fun(T a);对于int型,则会调用特例化版本的fun(int a)。一个特例化版本的本质是一个实例,而非函数的重载。因此,特例化不影响函数匹配。

  1.  偏特化
    

感觉像是介于普通模板和全特化之间,只存在部分的类型明确化,而非将模板唯一化。函数模板不能被偏特化。

类模板的偏特化:

template

class Test{

public:

 void print(){

   cout << "General template object" << endl;

 }

};

template<> //对int型特例化

class Test{

public:

void print() {

  cout << "Specialized template object" << endl;

}

};

与函数模板不同,类模板的特例化不必为所有模板参数提供实参。可以只指定一部分而非所有模板参数,这叫做类模板的偏特化或部分特例化(partial specialization)。例如,C++标准库中的类vector的定义:

template <typename T, typename Allocator>

class vector {

 /*......*/

};

// 部分特例化

template

class vector<bool, Allocator> {

 /*......*/

};

上例中一个参数被绑定到bool类型,而另一个参数仍未绑定,需由用户指定。注意,一个类模板的部分特例化版本仍然是一个模板,因为使用它时用户还必须为那些在特例化版本中未指定的模板参数提供实参。

小结:

l 类模板和函数模板都可以被全特化;

l 类模板能偏特化,不能被重载;函数模板能被重载,不能被偏特化。

40.3 模板类调用优先级
对主版本模板类、全特化类、偏特化类的调用优先级从高到低进行排序:

全特化类 > 偏特化类 > 主版本模板类

40.4 template meta
模板元编程是屠龙之技,可利用编译器对于template的解释是静态的这一特性,让编译器在编译时做计算,可有效提高程序的执行效率。假如只从实际工程出发,没有太大必要研究模板元编程。在此写这个主题,是让大家感受一下模板居然可以做这种事情。

假如将模板当成一门全新编程语言,最少应该具有以下特性

l 需要有基本的数据类型,比如数字、字符串、布尔值等

l 需要某种方法,将基础类型组合起来,表达更高级的概念。如C语言中的 struct, 就是一种组合方法。数组和字典也是组合方法。

l 需要流程控制,典型的有顺序、分支、循环。

模板元编程的基本原则:将负载由运行时转移到编译时,同时保持原有抽象层次。负载分为两类,一类是程序运行本身的开销,一类则是需要编写的代码,前者可理解为编译时优化,后者则是为提高代码复用度,从而提高编程效率。

40.5 模板的一些特点
· 模板包括函数模板和类模板,模板参数形式有:类型、模板型、非类型(整型、指针);

· 模板的特例化分完全特例化和部分特例化,实例将匹配参数集合最小的特例;

· 用实例参数替换模板形式参数称为实例化,实例化的结果是产生具体类型(类模板)或函数(函数模板),同一模板实参完全等价将产生等价的实例类型或函数;

· 模板一般在头文件中定义,可能被包含多次,编译和链接时会消除等价模板实例;

· template、typename、this 关键字用来消除歧义,避免编译错误或产生不符预期的结果;

· C++11 对模板引入了新特性:“>>”、函数模板也可以有默认参数、变长模板参数、外部模板实例(extern),并弃用 export template;

· C++ 模板是图灵完备的,模板编程是函数编程风格,特点是:没有可变的存储、递归,以“<>”为输入,typedef 或静态常量为输出;

· 编译期数值计算虽然实际意义不大,但可很好证明 C++ 模板的能力,可以用模板实现类似普通程序中的 if 和 while 语句;

· 一个实际应用是循环展开,虽然编译器可以自动循环展开,但可以让这一切更可控;

· 表达式模板和向量计算是另一个可加速程序的例子,它们将计算表达式编码到类型,这是通过模板嵌套参数实现的;

· 特性,策略,标签是模板编程常用技巧,可以使模板变得更加通用;

· 模板甚至可以获得类型的内部信息(是否有某个typedef),这是反射中的内省,C++在语言层面对反射支持很少(typeid),这不利于模板元编程;

· 可以用递归实现伪变长参数模板,C++11 变长参数模板背后的原理也是模板递归;

· 元容器存储元信息(如类型)、类型过滤过滤某些类型,它们是元编程的高级特性。

模板编程的缺点:

  1. 比较晦涩难懂。在复杂的地方使用让人更不容易读懂,且debug和维护都很麻烦。
  2. 经常会导致编译出错的信息不友好: 在代码出错的时候, 即使这个接口非常的简单, 模板内部复杂的实现细节也会在出错信息显示. 导致这个编译出错信息看起来非常难以理解。
  3. 模板如果使用不当,会导致运行时代码过度膨胀(源代码膨胀、二进制对象文件膨胀)。改进的方法是增加一些检查代码,让编译器及时报错, 使用特性、策略等让模板更通用,可能的话合并一些模板实例(如将代码提出去做成单独模板)
  4. 模板代码难以修改和重构。模板的代码会在很多上下文里面扩展开来, 所以很难确认重构对所有的这些展开的代码有用。

模板实参,那么用来替换实参就是可选的。

函数模板全特化:必须为原函数模板的每个模板参数都提供实参,且使用关键字template后跟一个空尖括号对<>,表明将原模板的所有模板参数提供实参。

template

void fun(T a) {

cout<< “The main template fun():” << a << endl;

}

template <> //对int型特例化

void fun(int a) {

cout<< “Specialized template for int type:”<< a << endl;

}

int main(){

fun(‘a’);

fun(10);

fun(9.15);

return 0;

}

对于除int型外的其他数据类型,都会调用通用版本的函数模板fun(T a);对于int型,则会调用特例化版本的fun(int a)。一个特例化版本的本质是一个实例,而非函数的重载。因此,特例化不影响函数匹配。

  1.  偏特化
    

感觉像是介于普通模板和全特化之间,只存在部分的类型明确化,而非将模板唯一化。函数模板不能被偏特化。

类模板的偏特化:

template

class Test{

public:

 void print(){

   cout << "General template object" << endl;

 }

};

template<> //对int型特例化

class Test{

public:

void print() {

  cout << "Specialized template object" << endl;

}

};

与函数模板不同,类模板的特例化不必为所有模板参数提供实参。可以只指定一部分而非所有模板参数,这叫做类模板的偏特化或部分特例化(partial specialization)。例如,C++标准库中的类vector的定义:

template <typename T, typename Allocator>

class vector {

 /*......*/

};

// 部分特例化

template

class vector<bool, Allocator> {

 /*......*/

};

上例中一个参数被绑定到bool类型,而另一个参数仍未绑定,需由用户指定。注意,一个类模板的部分特例化版本仍然是一个模板,因为使用它时用户还必须为那些在特例化版本中未指定的模板参数提供实参。

小结:

l 类模板和函数模板都可以被全特化;

l 类模板能偏特化,不能被重载;函数模板能被重载,不能被偏特化。

40.3 模板类调用优先级
对主版本模板类、全特化类、偏特化类的调用优先级从高到低进行排序:

全特化类 > 偏特化类 > 主版本模板类

40.4 template meta
模板元编程是屠龙之技,可利用编译器对于template的解释是静态的这一特性,让编译器在编译时做计算,可有效提高程序的执行效率。假如只从实际工程出发,没有太大必要研究模板元编程。在此写这个主题,是让大家感受一下模板居然可以做这种事情。

假如将模板当成一门全新编程语言,最少应该具有以下特性

l 需要有基本的数据类型,比如数字、字符串、布尔值等

l 需要某种方法,将基础类型组合起来,表达更高级的概念。如C语言中的 struct, 就是一种组合方法。数组和字典也是组合方法。

l 需要流程控制,典型的有顺序、分支、循环。

模板元编程的基本原则:将负载由运行时转移到编译时,同时保持原有抽象层次。负载分为两类,一类是程序运行本身的开销,一类则是需要编写的代码,前者可理解为编译时优化,后者则是为提高代码复用度,从而提高编程效率。

40.5 模板的一些特点
· 模板包括函数模板和类模板,模板参数形式有:类型、模板型、非类型(整型、指针);

· 模板的特例化分完全特例化和部分特例化,实例将匹配参数集合最小的特例;

· 用实例参数替换模板形式参数称为实例化,实例化的结果是产生具体类型(类模板)或函数(函数模板),同一模板实参完全等价将产生等价的实例类型或函数;

· 模板一般在头文件中定义,可能被包含多次,编译和链接时会消除等价模板实例;

· template、typename、this 关键字用来消除歧义,避免编译错误或产生不符预期的结果;

· C++11 对模板引入了新特性:“>>”、函数模板也可以有默认参数、变长模板参数、外部模板实例(extern),并弃用 export template;

· C++ 模板是图灵完备的,模板编程是函数编程风格,特点是:没有可变的存储、递归,以“<>”为输入,typedef 或静态常量为输出;

· 编译期数值计算虽然实际意义不大,但可很好证明 C++ 模板的能力,可以用模板实现类似普通程序中的 if 和 while 语句;

· 一个实际应用是循环展开,虽然编译器可以自动循环展开,但可以让这一切更可控;

· 表达式模板和向量计算是另一个可加速程序的例子,它们将计算表达式编码到类型,这是通过模板嵌套参数实现的;

· 特性,策略,标签是模板编程常用技巧,可以使模板变得更加通用;

· 模板甚至可以获得类型的内部信息(是否有某个typedef),这是反射中的内省,C++在语言层面对反射支持很少(typeid),这不利于模板元编程;

· 可以用递归实现伪变长参数模板,C++11 变长参数模板背后的原理也是模板递归;

· 元容器存储元信息(如类型)、类型过滤过滤某些类型,它们是元编程的高级特性。

模板编程的缺点:

  1. 比较晦涩难懂。在复杂的地方使用让人更不容易读懂,且debug和维护都很麻烦。
  2. 经常会导致编译出错的信息不友好: 在代码出错的时候, 即使这个接口非常的简单, 模板内部复杂的实现细节也会在出错信息显示. 导致这个编译出错信息看起来非常难以理解。
  3. 模板如果使用不当,会导致运行时代码过度膨胀(源代码膨胀、二进制对象文件膨胀)。改进的方法是增加一些检查代码,让编译器及时报错, 使用特性、策略等让模板更通用,可能的话合并一些模板实例(如将代码提出去做成单独模板)
  4. 模板代码难以修改和重构。模板的代码会在很多上下文里面扩展开来, 所以很难确认重构对所有的这些展开的代码有用。

建议模板编程用在少量的基础组件,基础数据结构上。并且使用模板编程时尽可能把复杂度最小化,尽量不要让模板对外暴露。在实现里面使用模板, 给用户暴露的接口并不使用模板, 这样能提高接口的可读性。并且应该在这

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值