25.C++- 泛型编程之函数模板(详解)
本章学习:
1)初探函数模板
2)深入理解函数模板
3)多参函数模板
4)重载函数模板
当我们想写个Swap()交换函数时,通常这样写:
void Swap(int& a, int& b)
{
int c = a;
a = b;
b = c;
}
但是这个函数仅仅只能支持int类型,如果我们想实现交换double,float,string等等时,就还需要从新去构造Swap()重载函数,这样不但重复劳动,容易出错,而且还带来很大的维护和调试工作量。更糟的是,还会增加可执行文件的大小.
所以C++引入了泛型编程概念
在C++里,通过函数模板和类模板来实现泛型编程(类模板在下章将讲解)
函数模板
- 一种特殊的函数,可通过不同类型进行调用
- 函数模板是C++中重要的代码复用方式
- 通过template关键字来声明使用模板
- 通过typename关键字来定义模板类型
比如:
template <typename T> //声明使用模板,并定义T是一个模板类型 void Swap(T& a, T& b) //紧接着使用T
{
T c = a;
a = b;
b = c;
}
当我们使用int类型参数来调用上面的Swap()时,则T就会自动转换为int类型.
函数模板的使用
- 分为自动调用和显示调用
例如,我们写了一个Swap函数模板,然后在main()函数里写入:
int a=;
int b=; Swap(a,b); //自动调用,编译器根据a和b的类型来推导 float c=;
float d=; Swap<float>(c,d); //显示调用,告诉编译器,调用的参数是float类型
初探函数模板
写两个函数模板,一个用来排序数组,一个用来打印数组,代码如下:
#include <iostream>
#include <string> using namespace std; template < typename T >
void Sort(T a[], int len)
{
for(int i=;i<len;i++)
for(int j=;j<i;j++)
if(a[i]<a[j])
{
T t=a[i];
a[i]=a[j];
a[j]=t;
}
} template < typename T >
void Println(T a[], int len)
{
for(int i=; i<len; i++)
{
cout << a[i] << ", ";
}
cout << endl;
} int main()
{
int a[] = {, , , , }; Sort(a, ); //自动调用,编译器根据a和5的类型来推导
Println<int>(a, ); //显示调用,告诉编译器,调用的参数是int类型 string s[] = {"Java", "C++", "Pascal", "Ruby", "Basic"}; Sort(s, );
Println(s, );
return ;
}
运行打印:
,,,,,
Basic,C++, Java,Pascal,Ruby,
深入理解函数模板
为什么函数模板能够执行不同的类型参数?
答:
- 其实编译器对函数模板进行了两次编译
- 第一次编译时,首先去检查函数模板本身有没有语法错误
- 第二次编译时,会去找调用函数模板的代码,然后通过代码的真正参数,来生成真正的函数。
- 所以函数模板,其实只是一个模具,当我们调用它时,编译器就会给我们生成真正的函数.
试验函数模板是否生成真正的函数
通过两个不同类型的函数指针指向函数模板,然后打印指针地址是否一致,代码如下:
#include <iostream> using namespace std;
template <typename T>
void Swap(T& a, T& b)
{
T c = a;
a = b;
b = c;
} int main()
{
void (*FPii)(int&,int&); FPii = Swap ; //函数指针FPii void (*FPff)(float&,float&); FPff = Swap ; //函数指针FPff cout<<reinterpret_cast<void *>(FPii)<<endl;
cout<<reinterpret_cast<void *>(FPff)<<endl;
//cout<<reinterpret_cast<void *>(Swap)<<endl;
//编译该行会出错,因为Swap()只是个模板,并不是一个真正函数
return ;
}
运行打印:
0x41ba98
0x41ba70
可以发现两个不同类型的函数指针,指向同一个函数模板,打印的地址却都不一样,显然编译器默默帮我们生成了两个不同的真正函数
多参数函数模板
在我们之前小节学的函数模板都是单参数的, 其实函数模板可以定义任意多个不同的类型参数,例如:
template <typename T1,typename T2,typename T3>
T1 Add(T2 a,T3 b)
{
return static_cast<T1>(a+b);
}
注意:
- 工程中一般都将返回值参数作为第一个模板类型
- 如果返回值参数作为了模板类型,则必须需要指定返回值模板类型.因为编译器无法推导出返回值类型
- 可以从左向右部分指定类型参数

接下来开始试验多参数函数模板
#include <iostream> using namespace std; template<typename T1,typename T2,typename T3>
T1 Add(T2 a,T3 b)
{
return static_cast<T1>(a+b);
} int main()
{
// int a = add(1,1.5); //该行编译出错,没有指定返回值类型 int a = Add<int>(,1.5);
cout<<a<<endl; // float b = Add<float,int,float>(,1.5);
cout<<b<<endl; //2.5 return ;
}
运行打印:
2.5
重载函数模板
- 函数模板可以像普通函数一样被重载
- 函数模板不接受隐式转换
- 当有函数模板,以及普通重载函数时,编译器会优先考虑普通函数
- 如果普通函数的参数无法匹配,编译器会尝试进行隐式转换,若转换成功,便调用普通函数
- 若转换失败,编译器便调用函数模板
- 可以通过空模板实参列表来限定编译器只匹配函数模板

接下来开始试验重载函数模板
#include <iostream> using namespace std; template <typename T>
T Max(T a,T b)
{
cout<<"T Max(T a,T b)"<<endl;
return a > b ? a : b;
} template <typename T>
T Max(T* a,T* b) //重载函数模板
{
cout<<"T Max(T* a,T* b)"<<endl;
return *a > *b ? *a : *b;
} int Max(int a,int b) //重载普通函数
{
cout<<"int Max(int a,int b)"<<endl;
return a > b ? a : b;
} int main()
{
int a=;
int b=; cout<<"a:b="<<Max(a,b) <<endl ; //调用普通函数 Max(int,int) cout<<"a:b="<<Max<>(a,b)<<endl; //通过模板参数表 调用 函数模板 Max(int,int) cout<<"1.5:2.0="<<Max(1.5,2.0)<<endl;
//由于两个参数默认都是double,所以无法隐式转换,则调用函数模板 Max(double,double) int *p1 = new int();
int *p2 = new int(); cout<<"*p1:*p2="<<Max(p1,p2)<<endl; // 调用重载函数模板 Max(int* ,int* ) cout<<"'a',100="<< Max('a',)<<endl;
//将char类型进行隐式转换,从而调用普通函数 Max(int,int) delete p1;
delete p2; return ;
}
运行打印:
int Max(int a,int b)
a:b=1
T Max(T a,T b)
a:b=1
T Max(T a,T b)
1.5:2.0=2
T Max(T* a,T* b)
*p1:*p2=2
int Max(int a,int b)
'a',=
接下来下章来学习: 26.C++- 泛型编程之类模板(详解)
25.C++- 泛型编程之函数模板(详解)的更多相关文章
- C++函数模板详解(一):概念和特性
函数模板是指这样的一类函数:可以用多种不同数据类型的参数进行调用,代表了一个函数家族.它的外表和普通的函数很相似,唯一的区别就是:函数中的有些元素是未确定的,这些元素将在使用的时候才被实例化.先来看一 ...
- 26.C++- 泛型编程之类模板(详解)
在上章25.C++- 泛型编程之函数模板(详解) 学习了后,本章继续来学习类模板 类模板介绍 和函数模板一样,将泛型思想应用于类. 编译器对类模板处理方式和函数模板相同,都是进行2次编译 类模板通 ...
- C++模板详解
参考:C++ 模板详解(一) 模板:对类型进行参数化的工具:通常有两种形式: 函数模板:仅参数类型不同: 类模板: 仅数据成员和成员函数类型不同. 目的:让程序员编写与类型无关的代码. 注意:模板 ...
- SQL中CONVERT()函数用法详解
SQL中CONVERT函数格式: CONVERT(data_type,expression[,style]) 参数说明: expression 是任何有效的 Microsoft® SQL Server ...
- c3p0-config.xml模板详解
c3p0-config.xml模板详解 <c3p0-config> <default-config> <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数.De ...
- C++ 类模板详解(一):概念和基本使用方式
与函数模板类似地(C++函数模板详解(一):概念和特性) ,类也可以被一种或多种类型参数化.例如,容器类就是一个具有这种特性的典型例子,它通常被用于管理某种特定类型的元素.只要使用类模板,我们就可以实 ...
- C++模板详解(三):参数化声明详解
在前两节中(C++模板详解(一).C++模板详解(二)),我们了解了函数模板和类模板的基本概念和使用方法.在这篇博文里,我们主要来详细地阐述一下"模板的参数声明"这个话题,并且也谈 ...
- 自写函数VB6 STUFF函数 和 VB.net 2010 STUFF函数 详解
'*************************************************************************'**模 块 名:自写函数VB6 STUFF函数 和 ...
- SQL Server数据库ROW_NUMBER()函数使用详解
SQL Server数据库ROW_NUMBER()函数使用详解 摘自:http://database.51cto.com/art/201108/283399.htm SQL Server数据库ROW_ ...
随机推荐
- CSS备战春招の二
CSS:层叠样式表 CSS注释形式:/* */ class 选择器有别于id选择器,class可以在多个元素中使用 id属性不要以数字开头,以数字开头在特定浏览器中不起作用 类名的第一个字符不要使用 ...
- 分布式日志收集系统:Flume
Flume知识点: Event 是一行一行的数据 1.flume是分布式的日志收集系统,把收集来的数据传送到目的地去. 2.flume里面有个核心概念,叫做agent.agent是一个java进程,运 ...
- asp.net core 五 SignalR 负载均衡
SignalR : Web中的实时功能实现,所谓实时功能,就是所连接的客户端变的可用时,服务端能实时的推送内容到客户端,而不是被动的等待客户端的请求.Asp.net SignalR 源码 ...
- c++中使用xercesc对xml进行schema校验
头文件 #pragma once #if !defined(AFX_A1CONTENTHANDLER_H__E0CFBC18_CCC1_42F3_B0A4_B03331AB9693__INCLUDED ...
- 序列化、反序列化(Serializable特性)
//需要被实例化的类 using System.Collections; using UnityEngine; [Serializable] public class SerializableClas ...
- Cucumber 相关资源
Cucumber support: https://cucumber.io/support yan1234abcd的专栏:http://blog.csdn.net/yan1234abcd/articl ...
- kubernetes实践之运行aspnetcore webapi微服务
1.预备工作 unbuntu 16.04 and above docker kubernetes 集群 2.使用vs2017创建一个web api应用程序,并打包镜像到本地. 3.推送本地镜像到doc ...
- delphi JPG图片 旋转 切边 缩放
unit UCutFigure_JPG; //JPG 切图 interface uses Windows, Messages, SysUtils, Variants, Classes, Graphic ...
- python全栈开发-Day2 布尔、流程控制、循环
python全栈开发-Day2 布尔 流程控制 循环 一.布尔 1.概述 #布尔值,一个True一个False #计算机俗称电脑,即我们编写程序让计算机运行时,应该是让计算机无限接近人脑,或者说人 ...
- PAT 个位数统计
描述 给定一个k位整数N = dk-1*10k-1 + ... + d1*101 + d0 (0<=di<=9, i=0,...,k-1, dk-1>0),请编写程序统计每种不同的个 ...