问题聚焦:
泛型编程是独立于变量类型的方式编写代码;
模板是泛型编程的基础。
本篇主要介绍模板的基础知识,包括:模板的定义和模板的实例化。

1 模版定义

必要性:
Demo

int compare(const string &v1, const string &v2)
{
if ( v1 < v2 ) return -1;
if ( v2 < v1 ) return 1;
return 0;
} int compare(const double &v1, const double &v2)
{
if ( v1 < v2 ) return -1;
if ( v2 < v1 ) return 1;
return 0;
}

很明显地看到,上面的两个函数几乎相同,事实上我也是复制第一个函数,然后修改参数类型作为第二个函数的。
这种写法的弊端显而易见。因此引入了模版函数。

定义模版函数
Demo

template <typename T>
int compare(const T &v1, const T &v2)
{
if ( v1 < v2 ) return -1;
if ( v2 < v1 ) return 1;
return 0;
}

定义: template + 模版形参列表
 模板形参列表是用尖括号括住的一个或多个模板形参(可以表示类型的类型形参,也可以表示常量表达式的非类型形参)的列表,形参之间用逗号隔开。
 类型形参跟在关键字class, typename之后定义
 class和typename没有区别。

使用:

Demo

int main()
{
// 模板形参为int
cout << compare(1, 0) << endl;
// 模板形参为string
string s1 = "hi", s2 = "world";
cout << compare(s1, s2) << endl;
return 0;
}

注意:inline模板函数的inline关键字要放在摸板型列表之后,返回类型之前,不能放在关键字template之前。

定义类摸板
Demo

template <class Type>
class Queue {
public:
Queue();
Type &front();
const Type &front () const;
void push (const Type &);
void pop();
bool empty() const;
};


使用:

Queue<int> qi;                                //用int代替Type
Queue< vector<double> > qc; //用 double型的vector代替Type
Queue<string> qs; //用string代替Type


模版形参
 模版形参选择的名字没有本质含义,如T可以替换为M或任意名字。
唯一含义:类型形参(表示未知类型)还是非类型形参(未知值)
作用域:

从声明为模版形参到模板声明或定义的末尾处使用。

屏蔽全局名字

Demo

typedef double T;
......
template <class T>
T calc (const T &a, const T &b)
{
T tmp = a;
....
return tmp;
}

T定义为double的全局类型别名将被名为T的类型形参所屏蔽。

限制:模版形参的名字只能在同一模板形参表中使用一次,且不能在当前模板内部重用,但是可以在不同的模板中重用。

声明:同一模板的声明和定义中,模板形参的名字不必相同。

typename和class的区别
相同含义,可以互换使用

非类型模板形参
模板形参不必都是类型。
在调用函数时,非类型形参将用值代替,值的类型在模版形参表中指定。
模板非类型形参是模板定义内部的常量值。
Demo

template <typename T, size_t N>
void array_init(T array_init(T (&parm)[N])
{
for (size_t i = 0; i != N; ++i)
{
parm[i] = 0;
}
} // 调用,当调用array_init时,编译器从数组实参计算非类型形参的值
int x[42];
double y[10];
array_init(x); // 等效 <int, 42>
array_init(y); // 等效 <double, 10>


编写泛型程序:减少对类型的要求
由于在函数模板内部完成的操作限制了可用于实例化该函数的类型,所以,编写模板代码时, 对实参类型的要求尽可能少是有益的。
两个原则:
  • 模板的形参是const引用:允许使用不允许复制的类型
  • 函数体中的测试只用<比较:减少对类型的要求(可能会有些类型,支持'<'但不支持'>')。

2 实例化
这里的实例化,需要和类对象的实例化区别开来。
模板的实例化:编译器用模板产生指定的类或函数的特定类型版本,产生模板的特定类型实例的过程称为实例化。
类的实例化:类模板形参是必需的。
Demo

//用string类型的对象创建Queue类
Queue<string> qs; // stirng代替Type的每次出现

函数模板实例化:编译器通常会为我们推断模板实参
Demo

int main()
{
compare(1, 0);
comapre(3.14, 2.7);
}



模板实参推断
从函数调用时的实参确定模板实参的类型和值的过程叫做模板实参推断。
1 多个类型形参的实参必须完全匹配
2 类型形参的实参的受限转换





只有两种情况会发生实参以匹配已有的实例化:

const转换:接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,无需产生新的实例化。

数组或函数到指针的转换:对数组或函数类型的实参用常规的指针转换。

template <typename T>     // 值传递
Tfobj(T, T);
template <typenam T>
T fref(const T&, const T&); // 引用传递 string s1("a value");
const string s2("another value");
fobj(s1, s2); // ok,const属性被忽略
fref(s1, s2); // ok , s1转换为const引用 int a[10], b[42];
fobj(a, b); // ok,数组转换为指针
fref(a, b); // error,形参为引用,数组不能转换为指针

3 应用于非模板实参的常规转换
类型转换的限制仅适用于类型为模板形参的那些实参。
4 获取模板实例化后的函数指针
Demo

template <typename T> int compare(const T&, const T&);
// pf1指向该模版函数的int型实例的地址
int (*pf1) (const int&, const in&) = compare;

指针pf1引用的是将T绑定到int的实例化。

函数模板的显式实参
无法推断模板实参的类型。这时需要显式指定模版形参所引用搞的类型或值。
1 指定显式模板实参
Demo

template <class T class U> ??? sum(T, U);

// 无法确定合适的返回类型
sum(2, 4L); // 适合该调用的为: U sum(T,U);
sum(3L, 4)); // 适合该调用的为:T sum(T, U); // 解决方案:在调用时,进行强制类型转换
int i; short s;
sum( static_cast<int>(s), i);


2 在返回类型中使用类型形参
返回类型与调用实参的类型不同,无法进行推断
Demo

template <typename T1, typename T2, typename T3>
T1 sum(T2, T3); // 调用,需要显式指定返回值的类型
long val3 = sum<long>(i, lng); // 还需要注意模版形参的顺序
template <typename T1, typename T2, typnename T3>
T3 sum(T1, T2)
// 在这种情况下的调用需要从左到右依次显式指定
long val2 = sum<int, long, long>(i, lng)


3 显式实参与函数模板的指针
当利用模板实例化重载函数的时候,需要显示指明调用的是哪一个实例
Demo

template <typename T>
int compare(const T&, const T&); // 实例化
void func(int(*) (const string&, const string&));
void func(int(*) (const int&, const int&));
// 显示指明哪一个func
func(compare<int>)(1, 2);



小结:
1 模板函数的声明和模板类的声明
2 模板形参和调用实参
3 类型推断,以及无法推断时的处理方法
4 实例化



参考资料:
《C++ Primer 4th》





C++ Primer(6) 模板和泛型编程(上)的更多相关文章

  1. C++ Primer 笔记——模板与泛型编程

    1.编译器用推断出的模板参数来为我们实例化一个特定版本的函数. 2.每个类型参数前必须使用关键字class或typename.在模板参数列表中,这两个关键字含义相同,可以互换使用,也可以同时使用. t ...

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

    继续浏览c++ primer 看到模板与泛型编程这章.就顺便把这几节的代码综合了下,对一个Queue队列模板的实现 贴一下代码(看完书.自己敲,忘记了哪再看下书) #include <ostre ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 阅读The Java® Language Specification需要知道的英文单词

      In any case/on any account  在任何情况下 “Varargs”是“variable number of arguments”的意思.有时候也被简单的称为“variable ...

  2. JavaScript数据结构-16.二叉树计数

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. python跳出多重循环

    # -*- coding=utf-8 -*- """ 如何结束多重循环,在单层循环中,可以用break跳出循环,那两层,三层呢? """ # ...

  4. 事务,约束,范式,视图,索引,pl/sql

    1.操作分类:  DML. DDL. DCL manipulation     definition   control 2.transction 事务 起始于DML,遇到 commit ,rollb ...

  5. 一起来做Chrome Extension《搭个架子》

    CEF - A simple Chrome Extension development falsework CEF是一个简单的Chrome Extension开发脚手架,它有如下功能: 模块化的结构, ...

  6. jdbc报java.lang.ClassNotFoundException: com.mysql.jdbc.Drive

    今天从开始写了一个jdbc连接mysql驱动的程序 真的是各种报错啊 首先这是代码 package com.dmeck; import java.sql.Connection; import java ...

  7. js的一些妙用

    在一个数组上  直接附加上另一个数组: Array.prototype.push.apply(array1, array2); 将对象转换成一个数组: Array.prototype.slice.ca ...

  8. [转]jQuery ListBox Plugin(ListBox插件)

    本文转自:http://www.cnblogs.com/think8848/archive/2011/09/28/2193990.html 转载请注明作者(think8848)和出处(http://t ...

  9. 14、IO (字节流、字符流)

    输入和输出 * A:输入和输出 * a: 参照物 * 到底是输入还是输出,都是以Java程序为参照 * b: Output * 把内存中的数据存储到持久化设备上这个动作称为输出(写)Output操作 ...

  10. C# 进程通信-命名管道

    之前看wcf服务的时候看到wcf有支持管道通信协议,之前不知道,最近刚好有用到这个,这里写个简单实例 .net有已经封装好的pip通信的对象NamedPipeServerStream 和NamedPi ...