一、为什么使用函数模板

假设我们在程序中需要比较两个变量的大小,但变量的类型可能是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. ABAP-DBC录屏

    *&---------------------------------------------------------------------**& ZXXL_MM02_01*& ...

  2. CodeIgniter Doctrine2基本使用(二)(转)

    CodeIgniter Doctrine2基本使用(二) 继上次写的一篇文章<CodeIgniter Doctrine2基本使用(一)>写到操作实体的之通过Channel这个实体向数据库表 ...

  3. MS datatype define(微软数据类型定义)

    这里根据MS文档整理了微软数据类型的定义, 参考文档:https://docs.microsoft.com/zh-cn/openspecs/windows_protocols/ms-dtyp/2463 ...

  4. c语言计算功能

    ---恢复内容开始--- #include <stdio.h> int main(){int a1,a2,a3,a4; printf("请输入数值"); a4=0;wh ...

  5. Rust 智能指针(一)

    Rust 智能指针(一) 1.Box<T> Box<T>是指向堆中的指针. fn main() { let box = Box::new(3); println!(" ...

  6. flex“深拷贝”

    以前在<ActionScript殿堂之路>上就看到过的“深拷贝”概念一直没有好好地在实战中用到过,但是最近在开发过程中,我发现我在编写VO数据对象时的一个老习惯很浪费我的编码时间,这个习惯 ...

  7. [TJOI2015]概率论[卡特兰数]

    题意 \(n\) 个节点二叉树的叶子节点的期望个数. \(n\leq 10^9\) . 分析 实际询问可以转化为 \(n\) 个点的不同形态的二叉树的叶子节点总数. 定义 \(f_n\) 表示 \(n ...

  8. Python码农福音,GitHub增加Python语言安全漏洞告警

    在 2017 年 GitHub 开始对托管在其网站的代码仓库和依赖库开始提供安全漏洞检查和告警,开始时候只支持 Ruby 和 JavaScript 语言的项目.根据 GitHub 官方数据显示截止目前 ...

  9. Jenkins + Gitlab + Ansible--playbook 代码上线流程(文末有免费视频)

      jenkins 构建是支持 git 选择分支 安装 Git Parameter 插件在系统管理中的插件管理 然后点击选插件,在右上角输入 Git Parameter,找到 Git Paramete ...

  10. Redis之数据类型大全

    一:String类型 1.set方法:设置key对应的值为string类型的value,如果该key已经存在,则覆盖key对应的value值.所以在redis中key只能有一个. 127.0.0.1: ...