目录

    • 定义一个通用模板
    • 模板特化和偏特化
    • 模板实例化与匹配
    • 可变参数模板

  泛型编程是指独立与任何类型的方式编写代码。泛型编程和面向对象编程,都依赖与某种形式的多态。面向对象编程的多态性在运行时应用于存在继承关系的类,一段代码可以可以忽略基类和派生类之间的差异。在泛型编程中,编写的代码可以用作多种类型的对象。面向对象编程所依赖的多态性称为运行时多态性,泛型编程所依赖的多态性称为编译时多态性或参数式多态性。


1 模板定义

1.1 函数模板

  • 模板定义以关键字 template 开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,形参之间以逗号分隔。模板形参表不能为空。
  • 模板函数的类型形参跟在关键字 class 或 typename 之后定义.在函数模板形参表中,关键字 typename 和 class 具有相同含义,可以互换使用,两个关键字都可以在同一模板形参表中使用
  • 函数模板可以用与非模板函数一样的方式声明为 inline。说明符放在模板形参表之后、返回类型之前,不能放在关键字 template 之前
  • 函数模板调用方式。在发生函数模板的调用时,不显示给出模板参数而经过参数推演,称之为函数模板的隐式模板实参调用(隐式调用)在发生函数模板的调用时,显示给出模板参数而不需要经过参数推演,称之为函数模板的显示模板实参调用(显示调用)。显示模板实参调用在参数推演不成功的情况下是有必要的。
  • 函数模板与函数重载。函数模板实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表,凡是函数体相同的函数都可以用这个模板来代替,不必定以多个函数。重载函数的参数个数、参数类型或参数顺序3者中必须至少有一种不同,函数返回值类型可以相同也可以不同,函数体可以相同。
 template<typename T>
inline bool isEqual(const T& t1, const T& t2) {
return t1 == t2;
}

1.2 类模板

  • 类模板也是模板,因此必须以关键字 template 开头,后接模板形参表
  • 除了模板形参表外,类模板的定义看起来与任意其他类问相似。类模板可以定义数据成员、函数成员和类型成员,也可以使用访问标号控制对成员的访问,还可以定义构造函数和析构函数等等。
  • 与调用函数模板形成对比,使用类模板时,必须为模板形参显式指定实参,类模板的形参不存在实参推演的。
 const size_t MAXSIZE = ;
template<class T>
class Stack{
private:
T elements[MAXSIZE];
public:
//others
};

1.3 模板参数

  • 类型模板形参:类型形参由关见字class或typename后接说明符构成,如template<class T> void getMaxVal(const T& a,const T& b){};其中T就是一个类型形参,类型形参的名字由用户自已确定。
  • 非类型模板形参:模板的非类型形参也就是内置类型形参,如template<class T, int X> greaterThanX(const T& a);其中int X就是非类型的模板形参。非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板的内部是常量。非类型的模板参数是有限制的,一般是一个整型,它们可以是常整数(包括枚举类型)或者指向外部链接对象的指针。浮点数和类对象是不允许作为非类型模板参数的。
  • 模板的默认参数。可以为类模板的类型形参提供默认值,但不能为函数模板的类型形参提供默认值。函数模板和类模板都可以为模板的非类型形参提供默认参数。类模板类型形参默认值和函数的默认参数一样,如果有多个类型形参则从第一个形参设定了默认值之后的所有模板形参都要设定默认值。类模板的类型形参默认值形式为:template<class T1, class T2=int> class A{};为第二个模板类型形参T2提供int型的默认值,在类模板的外部定义类中的成员时template 后的形参表应省略默认的形参类型。比如template<class  T1, class T2=int> class A{public: void h();}; 定义方法为template<class T1,class T2> void A<T1,T2>::h(){}
 template<typename T,int X = >
inline bool isEqualToX(const T& a) {
return a == X;
} template<class T,int MAXSIZE=>
class Stack {
private:
T elements[MAXSIZE];
public:
//others
};

2.模板特化与偏特化

  有时为了需要,针对特定的类型,需要对模板进行特化,也就是特殊处理。 例如,stack类模板针对bool类型,因为实际上bool类型只需要一个二进制位,就可以对其进行存储,使用一个字或者一个字节都是浪费存储空间的.。特化必须在同一命名空间下进行,可以特化类模板也可以特化函数模板,但类模板可以偏特化和全特化,而函数模板只能全特化。模板的偏特化是指需要根据模板的某些但不是全部的参数进行特化。严格的来说,函数模板并不支持偏特化,但由于可以对函数进行重载,所以可以达到类似于类模板偏特化的效果。模板实例化时会优先匹配”模板参数”最相符的那个特化版本。template < >告诉编译器这是一个特化的模板。

 template<class T,int MAXSIZE=>
class Stack {
private:
T elements[MAXSIZE];
public:
//others
}; //template specializations aim at bool
template<>
class Stack<bool>{ };
 1 template<typename T>
inline bool isEqual(const T t1, const T t2) {
return t1 == t2;
} //针对int型的指针做特化
template<>
inline bool isEqual(const int* p1,const int* p2){
return *p1 == *p2;
}

类模板的偏特化,例如c++标准库中的类vector的定义,这个偏特化的例子中,一个参数被绑定到bool类型,而另一个参数仍未绑定需要由用户指定。

 template <class T, class Allocator>
class vector { // … // };
template <class Allocator>
class vector<bool, Allocator> { //…//};

函数模板的偏特化,严格的来说,函数模板并不支持偏特化,但由于可以对函数进行重载,所以可以达到类似于类模板偏特化的效果。根据重载规则,对(a)进行重载。如果将(a)称为基模板,那么(b)称为对基模板(a)的重载,而非对(a)的偏特化。C++的标准委员会仍在对下一个版本中是否允许函数模板的偏特化进行讨论。
template <class T> void f(T); (a)
template < class T> void f(T*); (b)


3 模板实例化与匹配规则

3.1 隐式实例化。在使用模板函数和模板类时,不存在指定类型的模板函数和模板类的实体时,由编译器根据指定类型参数隐式生成模板函数或者模板类的实体称之为模板的隐式实例化。函数模板隐式实例化指的是在发生函数调用的时候,如果没有发现相匹配的函数存在,编译器就会寻找同名函数模板,如果可以成功进行参数类型推演,就对函数模板进行实例化。类模板隐式实例化指的是在使用模板类时才将模板实例化。

3.2 显示实例化。显示实例化也称为外部实例化。在不发生函数调用的时候将函数模板实例化,或者在不适用类模板的时候将类模板实例化称之为模板显示实例化。对于函数模板而言,不管是否发生函数调用,都可以通过显示实例化声明将函数模板实例化,定义函数模板为:template函数返回类型 函数模板名<实际类型列表>(函数参数列表),显示实例化为template void func<int>(const int&);类模板的显示实例化,对于类模板而言,不管是否生成一个模板类的对象,都可以直接通过显示实例化声明将类模板实例化,定义类模板格式为:template class 类模板名<实际类型列表>,显示实例化为template class theclass<int>;

3.3 匹配规则

(1) 类模板的匹配规则。最优化的优于次特化的,即模板参数最精确匹配的具有最高的优先权,每个类型都可以用作普通型(a)的参数,但只有指针类型才能用作(b)的参数,而只有void*才能作为(c)的参数。

template <class T> class vector{//…//}; // (a) 普通型
template <class T> class vector<T*>{//…//}; // (b) 对指针类型特化
template <> class vector <void*>{//…//}; // (c) 对void*进行特化

(2) 函数模板的匹配规则。非模板函数具有最高的优先权。如果不存在匹配的非模板函数的话,那么最匹配的和最特化的函数具有高优先权

template <class T> void f(T); // (d)
template <class T> void f(int, T, double); // (e)
template <class T> void f(T*); // (f)
template <> void f<int> (int) ; // (g)
void f(double); // (h)
bool b;
int i;
double d;
f(b); // 以 T = bool 调用 (d)
f(i,42,d) // 以 T = int 调用(e)
f(&i) ; // 以 T = int* 调用(f)
f(d); // 调用(g)


4.可变参数模板

参考:http://www.cnblogs.com/qicosmos/p/4325949.html

可变参数模板是C++11新增的特性之一,它对参数高度泛化,他能表示0到任意个数、任意类型的参数。可变模板参数之前会带有省略号,把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点。可变模版参数和普通的模版参数语义是一致的,所以可以应用于函数和类,即可变模版参数函数和可变模版参数类,然而,模版函数不支持偏特化,所以可变模版参数函数和可变模版参数类展开可变模版参数的方法还不尽相同。

4.1 可变模板参数函数与参数的展开

  • 递归函数方式展开参数包。通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数,递归终止函数正是用来终止递归的。
  • 逗号方式展开参数包
 #include <iostream>
using namespace std;
//递归终止函数
void print()
{
cout << "empty" << endl;
}
//展开函数
template <class T, class ...Args>
void print(T head, Args... rest)
{
cout << "parameter " << head << endl;
print(rest...);
} int main(void)
{
print(,,,);
return ;
}
 template <class T>
void printarg(T t)
{
cout << t << endl;
} template <class ...Args>
void expand(Args... args)
{
int arr[] = {(printarg(args), )...};
} expand(,,,);

4.2 可变模板参数类与参数展开

  可变参数模板类的参数包展开的方式和可变参数模板函数的展开方式不同,可变参数模板类的参数包展开需要通过模板特化和继承方式去展开,展开方式比可变参数模板函数要复杂。可变参数模板类是一个带可变模板参数的模板类,比如C++11中的元祖std::tuple就是一个可变模板类,它的定义如下,这个可变参数模板类可以携带任意类型任意个数的模板参数。

 template< class... Types >
class tuple;
std::tuple<int> tp1 = std::make_tuple();
std::tuple<int, double> tp2 = std::make_tuple(, 2.5);
std::tuple<int, double, string> tp3 = std::make_tuple(, 2.5, “”);
std::tuple<> tp;//可变参数模板的模板参数个数可以为0个,所以下面的定义也是也是合法的:

参考

  1. 模板特化和偏特化
  2. 泛化之美--C++11可变模版参数的妙用

C++中模板与泛型编程的更多相关文章

  1. C++ Primer 学习笔记_75_模板与泛型编程 --模板定义

    模板与泛型编程 --模板定义 引言: 所谓泛型程序就是以独立于不论什么特定类型的方式编写代码.使用泛型程序时,我们须要提供详细程序实例所操作的类型或值. 模板是泛型编程的基础.使用模板时能够无须了解模 ...

  2. C++ 模板与泛型编程

    <C++ Primer 4th>读书笔记 所谓泛型编程就是以独立于任何特定类型的方式编写代码.泛型编程与面向对象编程一样,都依赖于某种形式的多态性. 面向对象编程中的多态性在运行时应用于存 ...

  3. C++ Primer 学习笔记_76_模板与泛型编程 --模板定义[续]

    模板与泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...

  4. C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

    模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一 ...

  5. C++ Primer 学习笔记_77_模板与泛型编程 --实例化

    模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实 ...

  6. C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]

    模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还能够仅仅特化push和pop成员.我们将特化push成员以复制字符数组,而且特化pop成员以释放该副本使用的内存: ...

  7. C++ Primer 学习笔记_76_模板和泛型编程 --模板定义[继续]

    模板和泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...

  8. C++ Primer 学习笔记_79_模板与泛型编程 --模板编译模型

    模板与泛型编程 --模板编译模型 引言: 当编译器看到模板定义的时候,它不马上产生代码.仅仅有在用到模板时,假设调用了函数模板或定义了模板的对象的时候,编译器才产生特定类型的模板实例. 一般而言,当调 ...

  9. c++模板 与 泛型编程基础

    C++模板 泛型编程就是以独立于任何特定类型的方式编写代码,而模板是泛型编程的基础. (1)定义函数模板(function template) 函数模板是一个独立于类型的函数,可以产生函数的特定类型版 ...

随机推荐

  1. (原创)Hibernate 使用过程中(尤其是多对多关联中的级联保存和级联删除)的注意事项(基于项目的总结)

    一.先上知识点: 1.hibernate多对多关联关系中最重要的参数是(基于配置文件xxx.hbm.xml文件形式): 1):inverse属性,如果设置inverse=“true”就代表让对方参与维 ...

  2. C# 调用C++ CLR dll类库时,实现从 string 到 sbyte* 的转换

    问题描述 今天在做项目的时候碰到一个问题,就是用C++编写CLR类库dll的时候,C++的函数参数列表中包含一个char*的输出型参数,然而在C#调用该dll时候,会自动将函数的中的char*参数“翻 ...

  3. 二、安装并配置Kubernetes Master节点

    1. 安装配置Master节点上的Kubernetes服务 1.1 安装Master节点上的Kubernetes服务 yum -y install kubernetes 1.2 修改kube-apis ...

  4. 自适应XAML布局经验总结 (一)原则和页面结构设计

    XAML布局回顾 Grid和StackPanel是核心布局,尤其以Grid最为重要. Grid是网格布局,XAML的设计者有可能参考了Html里的Table设计了Grid布局,但进行了改进.Html中 ...

  5. SQL笔记---分页

    随用随想,随用随记. 通过实际应用掌握SQL语句. 一. SQL分页 1. 第一种方法:利用ID大于多少进行筛选 SELECT TOP 20        *FROM    dbo.WMS_Stock ...

  6. vsftpd 常见问题

    一.vsftp服务能开启却连接不上的解决办法: 用虚拟机装了centos,vsftp是用centos自带的.启动vsftd服务后却一直连不上,原因是被防火墙给挡了. 查看防火墙状态:/etc/init ...

  7. Spring 开发第一步

    经过今天上午的学习发现spring上手开发一个"hello world"真的非常简单. 开发环境搭建: 1.去spring官网下载spring-framework-3.2.11.R ...

  8. day65 Django模板语言

      常用语法 只需要记两种特殊符号: {{  }}和 {% %} 变量相关的用{{}},逻辑相关的用{%%}. 变量 {{ 变量名 }} 变量名由字母数字和下划线组成. 点(.)在模板语言中有特殊的含 ...

  9. python 中 使用sys模块 获取运行脚本时在命令行输入的参数

    在python项目的开发的过程中, 经常需要运行各种python脚本, 有时候还需要根据不同的使用情况输入不同的参数, 如果每次都去编辑一下脚本那就太麻烦,太耗费时间了, 这时就可以使用Python自 ...

  10. 双向链表的实现——java

    实现类: /** * Java 实现的双向链表. * 注:java自带的集合包中有实现双向链表,路径是:java.util.LinkedList * * @author skywang * @date ...