一、为什么使用函数模板

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

 //比较两个int型变量的大小
int compare(const int &a,const int &b){
return a>b?a:b;
}
//比较两个float型变量的大小
float compare(const float &a,const float &b){
return a>b?a:b;
}
//比较两个double型变量的大小
double compare(const double &a,const double &b){
return a>b?a:b;
}

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

二、什么是函数模板

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

语法:

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

实例演示:

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

 特别注意:

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

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

 template <typename type1,class type2>
void func(type1 a,type2 b){
cout<<a<<endl;
cout<<b<<endl;
}

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

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

 //inline函数的函数模板
template <typename type>
inline type func(type a){
a+=;
return a;
}

三、函数模板的实例化

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

特别注意:

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

 template <typename type>
type compare(const type &a,const type &b){
return a>b?a:b;
} int main(){
//实例化为 int compare(const int&,const int&)
cout<<compare(,)<<endl;
//实例化为float compare(const float&,const float&)
cout<<compare(1.2f,2.3f)<<end;
//实例化为double compare(const double&,const double&)
cout<<compare(1.2,2.3)<<endl;
return ;
}

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

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

四、函数模板的形参


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

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

特别注意:

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

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


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

特别注意:

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

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

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

compare("Tomwenxing","Jack");

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

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. 更改KVM虚拟机root的密码

    今天在使用qemu-kvm安装一个虚拟机,因为已经有一个虚拟机的image文件(qcow2格式的),所以创建虚拟机很简单,直接通过以下命令从image启动就行了. qemu-kvm -cpu host ...

  2. golden gate 加initial load 在rac 上的配置

    前言goldengate 11g 在oracle 11g rac 上的配置 (源是rac+asm , 目标是单数据库实例) 源端: 1. 配置tnsnames [oracle@rac1 admin]$ ...

  3. OC 知识:彻底理解 iOS 内存管理(MRC、ARC)

    1. 什么是内存管理 程序在运行的过程中通常通过以下行为,来增加程序的的内存占用 创建一个OC对象 定义一个变量 调用一个函数或者方法 而一个移动设备的内存是有限的,每个软件所能占用的内存也是有限的 ...

  4. 逻辑卷管理-LVM(Logical Volume Manager)

    一. 概念与由来 LVM:逻辑卷管理(Logical Volume Manager) 普通的磁盘分区管理方式在逻辑分区划分好之后就无法改变其大小,当一个逻辑分区存放不下某文件时,这个文件因为受上层文件 ...

  5. 乘积尾零——第九届蓝桥杯C语言B组(省赛)第三题

    原创 标题:乘积尾零 如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零? 5650 4542 3554 473 946 4114 3871 9073 90 4329 2758 7 ...

  6. lua通用数据类型

    TValue结构 TValue这个结构体是Lua的通用结构体,,Lua中的所有的数据都可以使用这个结构体来表示.很容易想到,在面向对象中,这个结构体是一个基类,派生出来的都是其他的子类. TValue ...

  7. JavaWeb基础—JS学习小结

    JavaScript是一种运行在浏览器中的解释型的编程语言 推荐:菜鸟教程一.简介js:javascript是基于对象[哪些基本对象呢]和和事件驱动[哪些主要事件呢]的语言,应用在客户端(注意与面向对 ...

  8. BootStrap 获得轮播中的索引和当前活动的焦点对象

    $('#myCarousel').on('slide.bs.carousel', function (event) { var $hoder = $('#myCarousel').find('.ite ...

  9. Kubernetes学习之路(十二)之Pod控制器--ReplicaSet、Deployment

    一.Pod控制器及其功用 Pod控制器是用于实现管理pod的中间层,确保pod资源符合预期的状态,pod的资源出现故障时,会尝试 进行重启,当根据重启策略无效,则会重新新建pod的资源. pod控制器 ...

  10. .net core的定时任务框架Timed Job

    参考文档:http://www.1234.sh/post/pomelo-extensions-timed-jobs 在该文档中介绍了怎么使用timed job,但是在使用db的时候会发生错误,错误一般 ...