一、为什么使用函数模板

假设我们在程序中需要比较两个变量的大小,但变量的类型可能是int、float或者double,此时为了满足程序的要求我们可能会在程序中编写多个函数,如:

  1. //比较两个int型变量的大小
  2. int compare(const int &a,const int &b){
  3. return a>b?a:b;
  4. }
  5. //比较两个float型变量的大小
  6. float compare(const float &a,const float &b){
  7. return a>b?a:b;
  8. }
  9. //比较两个double型变量的大小
  10. double compare(const double &a,const double &b){
  11. return a>b?a:b;
  12. }

在上面的代码中,我们发现编写的函数除了函数参数类型和返回值类型不同之外其余完全相同,这样导致的结果就是该程序代码的冗余性较高。为了解决这个问题,我们可以使用函数模板来编写与类型无关的函数以降低程序的冗余性

二、什么是函数模板

一个函数模板就是一个公式,可用来生成针对特定类型的函数体

语法:

  1. template <typename 形参名,typename 形参名,......>
  2. 返回值类型 函数名(参数列表){
  3. /*..........
  4. 函数体
  5. ...........*/
  6. }

实例演示:

  1. //compare函数的函数模板
  2. template <typename type>
  3. type compare(const type &a,const type &b){
  4. return a>b?a:b;
  5. }

 特别注意:

1.在函数模板的定义中,模板参数列表不能为空

2.在进行模板定义的时候可以用关键字class代替关键字typename,二者在这里是等价的。甚至可以在定义函数模板时同时使用这两个关键字,但还是推荐使用typename,因为这样会使程序的可读性更好

  1. template <typename type1,class type2>
  2. void func(type1 a,type2 b){
  3. cout<<a<<endl;
  4. cout<<b<<endl;
  5. }

3.模板参数(<>包裹的参数)表示在函数定义中使用到的类型或值。当使用模板时,我们(隐式地或显式地)指定模板实参,将其绑定到模板参数上

4.inline和constexpr的函数模板:将关键字inline或constexpr放在模板参数之后,返回值类型之前即可

  1. //inline函数的函数模板
  2. template <typename type>
  3. inline type func(type a){
  4. a+=;
  5. return a;
  6. }

三、函数模板的实例化

当我们调用一个函数模板时,编译器(通常)用函数实参来为我们推断模板实参,例如上面的compare函数,编译器会使用函数实参的类型来确定模板参数type的类型。

特别注意:

1.编译器会用推断出的模板参数来为我们实例化一个特定版本的函数,换句话说就是编译器会使用实际的模板实参来替代对应的模板参数来创建出一个模板的新“实例”

  1. template <typename type>
  2. type compare(const type &a,const type &b){
  3. return a>b?a:b;
  4. }
  5.  
  6. int main(){
  7. //实例化为 int compare(const int&,const int&)
  8. cout<<compare(,)<<endl;
  9. //实例化为float compare(const float&,const float&)
  10. cout<<compare(1.2f,2.3f)<<end;
  11. //实例化为double compare(const double&,const double&)
  12. cout<<compare(1.2,2.3)<<endl;
  13. return ;
  14. }

2.当编译器遇到一个模板定义时,它并不生成代码。只有当我们实例化出模板的一个特定版本时,编译器才会生成代码。

3.为了生成一个实例化的版本,编译器需要掌握函数模板的定义,因此与非模板代码不同,模板的头文件通常既包括声明也包括定义

四、函数模板的形参


A、类型形参:类型形参由关见字class或typename后接说明符构成,如:

  1. template <typename type> //类型形参
  2. void func(type a){
  3. ......
  4. }

特别注意:

1.我们可以将类型参数看做类型说明符,就像内置类型或类类型说明符一样使用。

2.不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,也就是说不能以func(int)的形式调用上面的函数模板func,只能以func(1)的形式调用。


B、非类型形参:非类型形参的参数表示一个值而非一个类型,通常情况下,我们通过一个特定的类型名而非关键字typename或class来指定非类型形参。

特别注意:

1.当一个模板被实例化时,非类型形参会被一个用户提供的或编译器推断出的值所替代,这些值必须是常量

  1. template <unsigned N,unsigned M>
  2. int compare(const char (&p1)[N],const char (&p2)[M]){ //p1和p2是对数组的引用,而N和M都表示数组的长度
  3. return strcpy(p1,p2);
  4. }

当我们调用这个版本的compare函数时“

  1. compare("Tomwenxing","Jack");

编译器会用字面值常量的大小来代替N和M,从而将模板实例化。另外编译器会在字符串字面常量的末尾插入一个空字符作为终结符,因而编译器最终实例化出的版本如下:

  1. int compare(const char (&p1)[],const char (&p2)[])

2.一个非类型形参可以是一个整型,也可以是一个指向对象或函数类型的指针或引用。其中绑定到非类型整型参数的实参必须是一个常量表达式而绑定到指针或引用非类型参数的实参必须具有静态的生存期

 3.函数模板中需要常量表达式的地方(比如说数组的大小),可以使用非类型形参

C++:模板——函数模板1的更多相关文章

  1. C++学习笔记35:函数模板

    函数模板 函数模板的目的 设计通用的函数,以适应广泛的数据型式 函数模板的定义格式 template<模板型式参数列表>返回值型式 函数名称(参数列表): 原型:template<c ...

  2. C++ template学习一(函数模板和模板函数)

    函数模板和模板函数(1)函数模板函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数.函数模板的声明形式为:templat ...

  3. 关于C++编译链接和模板函数

    一,关于编译链接编译指的的把编译单元生成目标文件的过程链接是把目标文件链接到一起的过程编译单元:可以认为是一个.c或者.cpp文件.每个编译单元经过预处理会得到一个临时的编译单元.预处理会间接包含其他 ...

  4. 25.C++- 泛型编程之函数模板(详解)

    本章学习: 1)初探函数模板 2)深入理解函数模板 3)多参函数模板 4)重载函数和函数模板 当我们想写个Swap()交换函数时,通常这样写: void Swap(int& a, int&am ...

  5. C++—模板(1)模板与函数模板

    1.引入 如何编写一个通用加法函数?第一个方法是使用函数重载, 针对每个所需相同行为的不同类型重新实现这个函数.C++的这种编程机制给编程者极大的方便,不需要为功能相似.参数不同的函数选用不同的函数名 ...

  6. C++解析(26):函数模板与类模板

    0.目录 1.函数模板 1.1 函数模板与泛型编程 1.2 多参数函数模板 1.3 函数重载遇上函数模板 2.类模板 2.1 类模板 2.2 多参数类模板与特化 2.3 特化的深度分析 3.小结 1. ...

  7. c++之旅:函数模板

    函数模板 函数模板主要是泛型在函数的中的应用,通过泛型可以让函数处理各种各样的数据类型 简单的列子 #include <iostream> using namespace std; tem ...

  8. C++模板之函数模板实例化和具体化

    模板声明 template<typename/class T>,  typename比class最近后添加到C++标准. 常规模板,具体化模板,非模板函数的优先调用顺序. 非模板函数(普通 ...

  9. C++ 函数模板与类模板(使用 Qt 开发编译环境)

    注意:本文中代码均使用 Qt 开发编译环境,如有疑问和建议欢迎随时留言. 模板是 C++ 支持参数化程序设计的工具,通过它可以实现参数多态性.所谓参数多态性,就是将程序所处理的对象的类型参数化,使得一 ...

随机推荐

  1. [图解tensorflow源码] TF系统概述篇

    Rendezvous 1. 定义在core/framework/rendezvous.h 2. A Rendezvous is an abstraction for passing a Tensor  ...

  2. 【vue】vue依赖安装如vue-router、vue-resource、vuex等

    方式一: 最直接的方式为在 package.json中添加如图依赖配置,然后项目 cnpm install即可 方式二: 根据vue项目的搭建教程,接下来记录下如何在Vue-cli创建的项目中安装vu ...

  3. GCD死锁,及同步、异步、串行和并行队列组合情形

      一.概述 1)队列用来存储代码任务,线程用来运行代码任务: 2)main()函数作为程序入口,整个程序默认运行在主线程中,程序代码任务默认存放在主队列中: 3)以下所谓阻塞线程是针对主线程而言(子 ...

  4. L2-025 分而治之(图)

    (这不会是我最后一天写算法题的博客吧...有点感伤...) 题目: 分而治之,各个击破是兵家常用的策略之一.在战争中,我们希望首先攻下敌方的部分城市,使其剩余的城市变成孤立无援,然后再分头各个击破.为 ...

  5. jQuery----奇偶选择器和索引选择器

    奇偶选择器: $( " #uu>li:odd").css( " backgroundColor "," red " ); 获取id为u ...

  6. kali linux修改更新源及更新

    1.修改sources.list源文件: leafpad /etc/apt/sources.list #aliyun 阿里云 deb http://mirrors.aliyun.com/kali ka ...

  7. Linux下开发python django程序(Session读写)

    1.登陆设置session信息 def loginsession(req): if req.method == 'POST': loginform = LoginForm(req.POST) if l ...

  8. PHP数据库的 增 删 查

    一.匹配数据库登录 步骤: 1.做一个普通的登录界面,注意提交方式为post. <!--登录界面--> <form action="chuli.php" meth ...

  9. 8-[表操作]--foreign key、表与表的关系

    1. foreign key (1)快速理解foreign key 员工信息表有三个字段:工号 姓名 部门 公司有3个部门,但是有1个亿的员工,那意味着部门这个字段需要重复存储,部门名字越长,越浪费 ...

  10. 5289: [Hnoi2018]排列

    5289: [Hnoi2018]排列 链接 分析: 首先将题意转化一下:每个点向a[i]连一条边,构成了一个以0为根节点的树,要求选一个拓扑序,点x是拓扑序中的第i个,那么价值是i*w[x].让价值最 ...