模板与泛型编程

--实例化

引言:

模板是一个蓝图,它本身不是类或函数。编译器使用模板产生指定的类或函数的特定版本号。产生模板的特定类型实例的过程称为实例化。

模板在使用时将进行实例化,类模板在引用实际模板类型时实例化,函数模板在调用它或用它对函数指针进行初始化或赋值时实例化。

1、类的实例化

当编写Queue<int>qi时,编译器自己主动创建名为Queue<int>的类。实际上,编译器通过又一次编写Queue模板,用类型int取代模板形參的每次出现而创建Queue<int>类。实例化的类就像已经编写的一样:

  1. template <typename Type> class Queue<Type>
  2. {
  3. public:
  4. Queue();
  5. int &front();
  6. const int &front() const;
  7. void push(const int &);
  8. void pop();
  9. bool empty() const;
  10.  
  11. private:
  12. //...
  13. };

假设要为string类型的对象创建Queue类,能够编写:

  1. Queue<string> qs;

在这个样例中,用string取代Type的每次出现。

【重点理解:】

类模板的每次实例化都会产生一个独立的类类型:为int类型实例化的Queue与随意其它Queue类型没有关系,对其它Queue类型的成员也没有特殊訪问权限!

2、类模板实參是必需的

想要使用类模板,就必须显式指定模板实參:

  1. Queue qs; //Error

类模板不定义类型仅仅有特定的实例才定义了类型。特定的实例是通过提供模板实參与每一个模板形參匹配而定义的。模板实參在用逗号分隔并用尖括号括住的列表中指定:

  1. Queue<int> qi;
  2. Queue<string> qs;

用模板类定义的类型总是包括模板实參。比如,Queue不是类型,而Queue<int>,Queue<string>是类型。

3、函数模板实例化

使用函数模板时,编译器一般会为我们判断模板实參:

  1. int main()
  2. {
  3. compare(1, 0); //将模板形參替换为int
  4. compare(3.14, 2.7); //将模板形參替换为double
  5. }

这个程序实例化了compare的两个版本号:一个用int取代T,还有一个用double取代T,实质上是编译器为我们编写了compare的这两个实例:

  1. int compare(const int &v1, const int &v2)
  2. {
  3. if (v1 < v2)
  4. return -1;
  5. if (v2 < v1)
  6. return 1;
  7. return 0;
  8. }
  9. int compare(const double &v1, const double &v2)
  10. {
  11. if (v1 < v2)
  12. return -1;
  13. if (v2 < v1)
  14. return 1;
  15. return 0;
  16. }

一、模板实參判断

要确定应该实例化哪个函数,编译器会查看每一个实參。假设相应形參声明为类型形參的类型,则编译器从实參的类型判断形參的类型。从函数实參确定模板实參的类型的过程叫做模板实參判断

1、多个类型形參的实參必须全然匹配

模板类型形參能够用作一个以上函数形參的类型。在这种情况下,模板类型判断必须为每一个相应的函数实參产生同样的模板实參类型

  1. template <typename T>
  2. int compare(const T &val1,const T &val2)
  3. {
  4. if (val1 < val2)
  5. return -1;
  6. else if (val2 < val2)
  7. return 1;
  8. return 0;
  9. }
  10.  
  11. int main()
  12. {
  13. short si;
  14. compare(si,1024); //Error:调用compare的实參类型不同,无法完毕正确判断
  15. }

假设compare的设计者想要同意实參的常规转换,则函数必须用两个类型形參来定义

  1. template <typename T,typename U>
  2. int compare(const T &val1,const U &val2)
  3. {
  4. if (val1 < val2)
  5. return -1;
  6. else if (val2 < val2)
  7. return 1;
  8. return 0;
  9. }
  10.  
  11. int main()
  12. {
  13. short si;
  14. compare(si,1024); //OK
  15. }

2、类型形參的实參的受限转换

一般而言,不会转换实參以匹配已有的实例化,相反,会产生新的实例。除了产生新的实例化之外,编译器仅仅会运行两种转换:

1)const转换:接受const引用或const指针的函数能够分别用非const对象的引用或指针来调用,无须产生新的实例化。假设函数接受非引用类型,形參类型实參都忽略const,即,不管传递const或非 const对象给接受非引用类型的函数,都使用同样的实例化。

2)数组或函数到指针的转换:假设模板形參不是引用类型,则对数组或函数类型的实參应用常规指针转换。数组实參将当作指向其第一个元素的指针,函数实參当作指向函数类型的指针

  1. template <typename T>
  2. T fobj(T,T);
  3. template <typename T>
  4. T fref(const T &,const T &);
  5.  
  6. string s1("a value");
  7. const string s2("another value");
  8.  
  9. fobj(s1,s2); //OK:call fobj(string,string),实參被复制
  10. fref(s1,s2); //OK:cal fref(const string &,const string &)
  11.  
  12. int a[10],b[42];
  13. fobj(a,b); //OK:call fobj(int *,int *)
  14. fref(a,b); //Error:实參没有办法转换成为pointer[指针]

3、应用于非模板实參的常规转换

注意以下的一段程序:

  1. template <typename Type>
  2. Type sum(const Type &op1,int op2)
  3. {
  4. return op1 + op2;
  5. }

由于op2的类型是固定的,在调用sum的时候,能够对传递给op2的实參应用常规转换:

  1. double d = 3.14;
  2. string s1("hiya"),s2("world");
  3. cout << sum(1024,d) << endl;
  4. cout << sum(1.4,d) << endl;
  5. cout << sum(s1,s2) << endl; //Error:没有从string到int的转换

4、模板实參判断与函数指针

能够使用函数模板对函数指针进行初始化或赋值,此时,编译器使用指针的类型实例化具有适当模板实參的模板版本号。

比如,假定有一个函数指针指向返回int值的函数,该函数接受两个形參,都是 constint 引用,能够用该指针指向compare的实例化:

  1. template <typename T>
  2. int compare(const T &,const T &);
  3. int (*pf1)(const int &,const int &) = compare;

pf1的类型是一个指针,指向“接受两个constint & 类型形參并返回int值的函数”,形參的类型决定了T的模板实參的类型:T的模板实參为int型,指针 pf1引用的是将 T绑定到 int的实例化。

获取函数模板实例化的地址的时候,上下文必须是这种:它同意为每一个模板形參确定唯一的类型或值。

假设不能从函数指针类型确定模板实參,就会出错:

  1. void func(int (*)(const string &,const string &));
  2. void func(int (*)(const int &,const int &));
  3. func(compare); //Error:通过查看func的形參类型不可能确定模板实參的唯一类型。

对func的调用能够实例化下列函数中的随意一个:

  1. compare(const string&, const string&)
  2. compare(const int&, const int&)

由于不能为传给func的实參确定唯一的实例化,该调用会产生一个编译时(或链接时)错误!

  1. //P540 习题16.22
  2. template <class Type>
  3. Type calc(const Type *arr,int size);
  4.  
  5. template <class Type>
  6. Type fcn(Type p1,Type p2);
  7.  
  8. int main()
  9. {
  10. double dobj;
  11. float fobj;
  12. char cobj;
  13. int ai[5] = {511,16,8,64,343};
  14.  
  15. calc(&cobj,'c');
  16. calc(&dobj,fobj);
  17. fcn(ai,ai + sizeof(ai)/sizeof(*ai));
  18. }

C++ Primer 学习笔记_77_模板与泛型编程 --实例化的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

  7. 高放的c++学习笔记之模板与泛型编程

    函数模板 作用 有很多时候参数的类型以及返回值的类型是可变的,我们通过定义模板来让函数能更灵活的运用. 我们设计一个比较函数,如果能比较的两个参数是int型的,两个参数也可能都是string型的,单独 ...

  8. OpenCV 学习笔记(模板匹配)

    OpenCV 学习笔记(模板匹配) 模板匹配是在一幅图像中寻找一个特定目标的方法之一.这种方法的原理非常简单,遍历图像中的每一个可能的位置,比较各处与模板是否"相似",当相似度足够 ...

  9. Python Flask学习笔记之模板

    Python Flask学习笔记之模板 Jinja2模板引擎 默认情况下,Flask在程序文件夹中的templates子文件夹中寻找模板.Flask提供的render_template函数把Jinja ...

随机推荐

  1. 用C/C++扩展你的PHP(转)

    简 介 英文版下载: PHP 5 Power Programming PHP取得成功的一个主要原因之一是她拥有大量的可用扩展.web开发者无论有何种需求,这种需求最有可能在PHP发行包里找到.PHP发 ...

  2. 【nodejs学习】3.进程管理及异步编程

    进程管理 1.调用终端命令实现目录目录拷贝 var child_procress = require('child_procress'); var util = require('util'); fu ...

  3. iOS_SN_CocoaPods使用详细说明( 转)

    一.概要 iOS开发时,项目中会引用许多第三方库,CocoaPods(https://github.com/CocoaPods/CocoaPods)可以用来方便的统一管理这些第三方库. 二.安装 由于 ...

  4. 设置Chrome和IE搜索栏的默认搜索引擎

    由于本人比较喜欢用Google香港作为自己的默认搜索引擎,Chrome里面可以轻松设置. ======================== Chrome ======================= ...

  5. git 笔记记录

    分布式版本控制系统Git 是一套内容寻址文件系统,从核心上来看不过是简单地存储键值对.一: git 本地clone 一个仓库    1. 直接clone一个仓库:        $: git clon ...

  6. UML类图常见的几种关系

    关系:泛化(Generalization),实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency) ...

  7. 带权并查集 poj1182

    首先要注意核心代码 int find(int i){    if(i == fa[i])        return fa[i];    int tt = find(fa[i]);    num[i] ...

  8. dijkstra堆优化模板

    #include<iostream> #include<cmath> #include<algorithm> #include<cstring> #in ...

  9. PHP扩展开发(4) - 多类扩展

    由于函数和单类的扩展,网上一搜一大片,这里就不再叙述了. 这里特别感谢laruence(鸟哥)开源的yaf扩展,解决困扰我多时的多类问题,还在看他的代码学习中,这里是对多类写法学习的一个阶段总结.   ...

  10. Android再学习-便签开发小结-20141119

    这几天的便签开发,首先遇到的问题就是数据库操作问题.现在已经可以读写数据库了,并能将数据放在正确的位置显示. 专门建立了一个数据库操作的包,命名为"...database".新建一 ...