C++之函数模板

2023-11-19

1、什么是模板?模板有什么作用?
模板分为函数模板和类模板。函数模板是对函数功能框架的描述,具体功能由实际传递的参数决定。
有了函数模板,编译器就会根据模板自动生成多个函数名相同,参数列表不同的函数,不需要手动写;
例:求一个矩形面积
当传入的长、宽均是正整数时,我们需要这样写函数:

void Rectangle(int a,int b)
{
   int s;
   s=a*b;
   cout<<"s="<<s<<endl;
}

但如果传入的长、宽都是带小数的时候,我们就需要这样写:

void Rectangle(double a,double b)
{
   double s;
   s=a*b;
   cout<<"s="<<s<<endl;
}

可以发现,这样写实在繁琐,形式都是一样的,还需要写两边。为了解决这个问题,就有了函数模板。
2、函数模板该怎样使用?
函数模板的写法如下:

template<typename 类型参数1,typename 类型参数2>
返回值类型 模板名(形参名)
{
   函数体;
}

对于上边的程序,我们可以定义这样一个函数模板:

template<typename T>
void Rectangle(T&x,T&y)
{
  T s;
  s=x*y
  cout<<"s"<<s<<endl;
}
T是类型参数,代表传入参数的类型。函数在由模板生成函数时,会自动根据实参的类型对模板中的类型参数进行替换。

3、函数模板和模板函数有什么区别?
有模板实例化的到的具体函数叫做模板函数(实例化:编译器由模板自动替换生成具体函数的过程叫做实例化);
函数模板会在编译期根据使用情况生成对应的函数
模板不编译;
但是模板生成的函数指令会编译;
模板中的语法错误,会在生成对应的指令时候被编译出错误;
函数模板有类型自推的能力,使用函数模板可以不用传模板类型参数;
对于上边的程序:

template<typename T>
void Rectangle(T&x,T&y)
{
  T s;
  s=x*y
  cout<<"s"<<s<<endl;
}

这是函数模板

void Rectangle(int a,int b)
{
   int s;
   s=a*b;
   cout<<"s="<<s<<endl;
}
void Rectangle(double a,double b)
{
   double s;
   s=a*b;
   cout<<"s="<<s<<endl;
}

这是模板函数

下边我们来看一段小代码:

template<typename T>
bool compare(T a, T b)
{
	cout << "template<typename T> bool compare(T a, T b)" << endl;
	cout << typeid(T).name() << endl;
	return a > b;
}
int main()
{
	compare(10, 20);
	compare(10.2, 20.3);
	//compare<int>(10, 20);
	//compare<double>(10.2, 20.3);
}

在编译器对模板进行实例化时,模板的类型参数并非只能通过传入实参来判断,还可以直接在传参的时候就指明类型参数该实例化为哪种类型。(上边程序屏蔽的部分)。
此处实例化的模板函数是:

bool Compare(double a,double b);

还需要说明的是:在指定类型的时候,可以指定多个类型,如:

compare<doublefloat>(10.2, 20)

即使不指定,编译器也会替换为相应的类型,即:

compare<double>(10.2, 20)//20的类型为int

同样,如果不指定,还是根据实参进行识别的话,也会识别出不同的类型,即:

  compare(10.2, 20);//10.2 为double,20为int

示例:写一个冒泡排序的模板

template<typename T>
void swap1(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
template<typename T>
void sort(T* arr, int len)
{
	cout << "void sort(T *arr,int len)" << endl;
	cout << typeid(T).name() << endl;
	if (NULL == arr)
	{
		return;
	}
	for (int i = 0; i < len-1; i++)
	{
		for (int j = 0; j < len - i - 1; j++)
		{
			if (!(arr[j] > arr[j + 1]))
			{
				swap1(arr[j], arr[j + 1]);
			}
		}
	}
}

int main()
{
	int arr[] = { 23,45,34,11,67,89,4,3,90,21 };
	double arr[] = { 23,45,34,11,67,89,4,3,90,21 };
	int len = sizeof(arr) / sizeof(arr[0]);
	sort(arr, len);
	return 0;
}

冒泡排序函数模板在编译期会自动生成以下两种函数:

void sort(int *arr,int len);
void sort(double *arr,int len);

4、函数模板的特例化
问题:特例化是什么?为什么要特例化?
答:特例化顾名思义就是函数模板的特殊实例化,它是为了完成一半模板函数不能完成的特殊任务。例:
还是刚才那个代码:

template<typename T>
bool compare(T a, T b)
{
	cout << "template<typename T> bool compare(T a, T b)" << endl;
	cout << typeid(T).name() << endl;
	return a > b;
}
int main()
{
	if (compare("aaa", "sss"))//会直接比较地址,比较结果不可信
	{
		cout << "aaa > sss" << endl;
	}
	else
	{
		cout << "sss > aaa" << endl;
	}
	进行一般的数值比较是,上边的模板函数就可以了,但如果两个实参都是字符串的话,它们的实参类型就位`const char*`类型。
	众所周知,进行字符串比较时,是按照字符一个一个进行比较的,这样的结果才可信,但是如果此时我们还是调用上边的模板函数来比较的话,它就不是比较的字符串了,它比较的是整个字符串的地址。这样造成的结果就是,左边的永远比右边的大
}

上述代码一眼就能看出来sss大,正确答案应该是sss > aaa,打印结果如下:
在这里插入图片描述
我们发现,这个结果明显不正确。
在这里插入图片描述
通过观察地址可以发现,aaa的地址比sss大,这就说明了对于字符串这种特殊的实参,我们不能用普通的模板函数来比较,必须另写一个模板函数的特例化来解决这些问题,即:

template<>
bool compare(const char* a, const char* b)
{
	cout << "bool compare(const char* a, const char* b)" << endl;
	return strcmp(a, b) > 0;
}

执行结果如下:
在这里插入图片描述
我们发现结果正确了。

解释:特例化并不是对函数模板进行重载,相反,它也只是函数模板实例化的特殊模板函数。这种特例化的模板函数在写时,需要直接在形参参数列表中写明参数类型,不能再使用类型参数。
特例化分为部分特例化和全部特例化。全部特例化就是上边这种,将两个参数都写明参数类型。部分特例化就是只将一部分的参数写明参数类型,如:

template<T>
bool compare(T a,int b);

5、类模板
还是先明确一个定义,类模板的实例化就是模板类。
类模板,实际上是建立一个通用类,内部的数据成员,成员函数的返回值类型和参数类型不具体说明,用类型参数来代表。当使用类模板定义对象时,系统会根据实参的类型去替换模板中的类型参数,从而实例化出一个模板类,用以实现具体的功能。
其格式如下:

template<typename 类型参数>
class 类名
{
   类成员说明
};

当需要实现具体功能时,就需要建立具体的类模板,如:求矩形面积

template<typename T>
class Rectangle
{ 
publicRectangle(T a,T b)
    { 
        x=a;
        y=b;
    }
    T Area()
    {
        return a*b;
    }
private:
    int x,y;
}
int main()
{
	Rectangle<int>a(10, 20);
}

注意:类模板
类模板不编译
会在编译期根据使用方式,生成对应的类代码
类模板中没有使用到的成员方法不会在编译器生成对应的指令
类模板使用时必须加上模板类型参数,类模板无法自己推导类型参数
为什么类模板的成员方法不能再其它cpp文件中实现?
类模板需要在编译时期将使用到的成员方法生成对应的指令
编译器是指针对单文件的
如果将类模板的成员方法实现在其他文件,编译期使用到该方法的文件不可见,就无法生成对应的指令----就会报无法解析的外部符号

再看一个代码:

template <typename T>
class Arr
{
public:
	Arr(T x,T y)
	{
	    a=x;
	    b=y;
		cout << "Arr()" << endl;
	}
	T Area();
private:
    T a,b;
};

//类模板的成员方法类外实现
template<typename T>
void Arr<T>::Area()
{
     return x*y;
	cout << "void Arr<T>::Area()" << endl;
}

int main()
{
    Arr<int>A(1,2);
    Arr<float>B(2,3);
    Arr<double>C(3,4);
    return 0;
}

上述代码中,类模板进过实例化之后产生了三个模板类,并产生了A、B、C三个对象。
注意:类的成员函数在类外实现时需要在函数定义之前进行模板声明,在成员函数名前写上“类名::”;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++之函数模板 的相关文章

  • C# 中两种不同类型的列表

    我目前在为客户提供购物车时遇到问题 他希望能够在 CartItems 之间添加文本 所以我想知道是否有某种方法仍然只有一个列表 我的解决方案是有两个列表 其中一个是 IList 类型 在计算购物车的重量和总体价格时会迭代 而另一个 ILis
  • 如何指定 set precision 舍入

    当流到 std 输出时 我可以指定 set precision 对双精度值进行舍入吗 ofile lt lt std setprecision 12 lt lt total run time TIME lt lt n Output 0 75
  • 如何从更高级别启动用户级别的 Exe

    我希望一个进程始终在用户级别运行 当它由以管理员级别运行的安装程序 自定义 而不是 msi 启动时 或者当用户登录时 环顾四周 我不确定这是否可能 最简单的方法是有 2 个进程 一种是普通用户 它启动提升 管理进程 然后管理进程可以使用 I
  • 在单个 C# 泛型方法中返回可为 null 和 null?

    C 泛型方法是否可以返回对象类型或 Nullable 类型 例如 如果我有一个安全的索引访问器List我想返回一个值 稍后我可以使用以下任一方法检查该值 null or HasValue 目前我有以下两种方法 static T SafeGe
  • C 语言中的套接字如何工作?

    我对 C 中的套接字编程有点困惑 You create a socket bind it to an interface and an IP address and get it to listen I found a couple of
  • MigraDoc 项目符号列表(漏洞)

    在我的解决方案中 我在 PDF 文件中使用项目符号列表 它看起来像这样 Solcellepaneler kr ver hverken autoriseret service eller tidskr vende vedligehold So
  • 枚举 EMF 时丢失文本

    我在列举发票 emf http www mediafire com kdjwvvo7odyvwa6并将其复制到另一个但文本丢失了 令人惊讶的是 当我将其输出到窗口时 它绘制得非常完美 int CALLBACK EnhMetaFileProc
  • 在 PHP 扩展中,推荐从 std::string 返回值的方法

    我们有一个简单的 PHP 函数 其目的是调用 C 自由函数std string callLibrary std string 并返回其std string返回值 目前看起来是这样的 PHP FUNCTION call library cha
  • 如何让BackgroundWorker返回一个对象

    我需要做RunWorkerAsync 返回一个List
  • 如何在Qt3D中优化点云渲染

    我正在尝试使用 Qt3D 显示大型点云 20M pts 我第一次发现这个图书馆https github com MASKOR Qt3DPointcloudRenderer https github com MASKOR Qt3DPointc
  • 用于轻松动态反射的 C# 库

    是否有任何库 例如开源项目等 可以更轻松地使用复杂的反射 例如动态创建对象或类 检查实例等 Thanks 有一个LinFu http www codeproject com KB cs LinFuPart1 aspx可用的库除了反射之外还可
  • C 风格强制转换与内在强制转换

    假设我已经定义了 m256d x我想提取低 128 位 我会做 m128d xlow mm256 castpd256 pd128 x 然而 我最近看到有人这样做 m128d xlow m128d x 是否有用于演员的首选方法 为什么要用第一
  • C++在子类中调用虚方法

    我有以下课程 class A protected A inner public virtual void doSomething 0 class B public A void doSomething if inner NULL inner
  • 如何使用 Linq to Sql 修剪值?

    在数据库中 我有一个名为 联系人 的表 名字和其他此类字符串字段设计为使用 Char 数据类型 不是我的数据库设计 我的对象 Contact 映射到属性中的字符串类型 如果我想做一个简单的测试 通过 id 检索 Contact 对象 我会这
  • 为什么这段代码不会产生编译错误?

    template
  • 扩展一个类

    编辑回答 虽然我最初的问题并没有完全按照康拉德 鲁道夫提供的答案所解决的方式解释我的需求 但他 无意或有意 基本上为我写了我想写的内容 类本身不会被扩展 但通过使类了解新函数来扩展其功能 这些新函数允许它 类 处理更广泛的问题 我非常感谢您
  • 在 Ubuntu 16.04 上编译 PCL 1.7,CMake 生成的 Makefile 中出现错误

    我正在尝试让 PCL 1 7 点云库 而不是其他 pcl 在 Ubuntu 16 04 上运行 我最终希望用于 C 的东西 但现在我只是想让这些例子工作 我使用的是 Ubuntu GNU 5 3 1 附带的默认编译器和 Cmake 版本 3
  • 比较 C# 中的对象属性[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的答案或互动
  • 在 C# 中设置风扇速度

    我知道以前有人问过这个问题 但我似乎无法让它发挥作用 我已调用以下内容 using System Management using System Management Instrumentation using System Runtime
  • Eclipse CDT C/C++:包含另一个项目的头文件

    我在 Eclipse CDT 中有两个 C 项目main and shared In shared我有一个名为calc h 我想在中使用这个标头main 所以我做了以下事情 added include calc h到相关文件main In

随机推荐

  • Win11如何下载安装java?

    一 问题描述 我在复现论文代码的时候 遇到了这样的问题 我没有下载java 那么该如何解决呢 下载 Java 的作用是为了能够在计算机上运行使用 Java 语言编写的应用程序 Java 是一种广泛使用的编程语言 可用于开发各种类型的应用程序
  • CryptoJS与JSEncrypt 加密算法

    crypto js进行AES加密 安装 npm i save crypto js jsencrypt进行RSA加密 安装 npm i save jsencrypt 官网 https github com travist jsencrypt
  • 微软Imagine Cup 2013大赛中国区CSDN高校俱乐部校区比赛成绩及获奖名单

    微软 Imagine Cup 2013 大赛已接近尾声 CSDN高校俱乐部首次参加此大赛 在中国赛区的比赛中 CSDN高校俱乐部校区取得了令人骄傲的成绩 在此向所有的参赛同学表示祝贺和感谢 同时 非常感谢各俱乐部的指导老师 主席 同学对CS
  • 路由期末复习(二)—配置命令

    这篇就专门说说关于配置的知识点 了解基础知识指路 目录 路由器 Telnet服务配置命令 路由器 SSH服务配置命令 SSH配置例子 重点 一图理解SSH配置 用FTP传输文件 使用TFTP传输文件 VLAN的基本配置 配置Hybrid端口
  • APP、软件版本号的命名规范与原则

    APP 软件版本号的命名规范与原则 为了在软件产品生命周期中更好的沟通和标记 我们应该对APP 软件的版本号命名的规范和原则有一定的了解 1 APP 软件的版本阶段 Alpha版 也叫 版 此版本主要是以实现软件功能为主 通常只在软件开发者
  • python中mysql的用法_Python中MySQL用法

    Python中MySQL用法 一 注意事项查看系统版本 arch命令 查看系统是64位还是32位 使用cat etc system release查看内核版本 注意安装MySQL的版本企业版 付费 社区版 免费 MariaDB 注意安装之后
  • java计算机毕业设计基于springboo+vue的汉服文化宣传活动交流网站(汉服社团)

    项目介绍 近年来 随着个人计算机的普及以及互联网的飞速发展 互联网逐渐成为人们获取信息的重要渠道 互联网的便捷性与实时性等特征 在方便人们获取自己感兴趣信息的同时 也在很大程度上为企事业单位节约了大量人力 物力 财力等运营成本 汉服交流网站
  • 【笔记】下单但未支付的订单倒计时自动取消逻辑实现

    平常我们都用过淘宝 京东这些电商平台 同时肯定也在这些平台上面下过单 这种情况不保证大家都有遇到过 但做开发的 肯定也知道有这个环节的存在 确认货品配置无误之后 我们都会点击购买 随之而来的就是一个结算页 让你确认商品信息 收货地址 价格等
  • ElementUi的el-tree组件样式修改

    ElementUi的el tree组件样式修改 需求如下 下拉图标的修改 element ui中的原本的基本样式是这样的 所以第一步呢 就是要把这个下拉按钮的样式修改成加号 在vue文件中 修改样式即可 vue的项目在写样式的时候 回家上s
  • join表连接的三种算法思想:Nested-Loop Join和Index Nested-Loop Join和Block Nested-Loop Join和BKA

    一 Nested Loop Join 在Mysql中 使用Nested Loop Join的算法思想去优化join Nested Loop Join翻译成中文则是 嵌套循环连接 举个例子 select from t1 inner join
  • ChatGPT能为留学生做什么?错误使用有何后果?

    随着AI人工智能行业的迅速发展 越来越多的学生开始利用ChatGPT等软件来获得更高效便利的论文和作业辅助 然而 我们需要认识到一个严肃的问题 学生是否过度依赖AI助手来完成毕业论文 近期出现的Turnitin AI Detector是一个
  • 在Windows下使用vs2019编译libjpeg库

    一 库的编译 1 下载 libjpeg 源码 这里我下载的是 jpegsr9e zip 2 解压源码 3 进入解压后的目录 找到 makefile vs 文件 用文本编辑器打开并编辑 找到 语句 include
  • 设备管理过程

    复杂度2 5 机密度2 5 最后更新2021 04 19 AIX中对设备会有如下五个操作 define aix下能看到设备的定义 但驱动程序并没有加载或初始化 该设备不可用 lsdev看到设备时defined 很多逻辑设备 vg lv等 只
  • CTF练题(5)word隐写基础题,jpg图片隐写,敲击码解密

    2022 11 2 两道misc题目 题目一 word隐写基础 题目信息如下 以及一个无法打开的word文档 解题步骤 1 将该word文档拖入010Editor中进行分析 发现文件头显示为PK 压缩文件 将该文档后缀改为 zip 保存到桌
  • go-zero使用Etcd进行服务注册代码分析

    代码分析 github com tal tech go zero v1 2 3 core discov publisher go package discov import github com tal tech go zero core
  • ld链接器的--start-group和--end-group参数说明

    start group archives end group The archives should be a list of archive files They may be either explicit file names or
  • OpenLayers与Bootstrap样式冲突的解决

    在引入Bootstrap响应式布局样式后 OpenLayers图层瓦片会显示异常 在页面中加入以下样式可以解决 参见 http openlayers org dev examples bootstrap html
  • linux网络95值工具,Linux下网络故障排查工具之ping

    服务器运维人员在日常运维服务器的过程中经常会遇到服务器网络故障 有服务器硬件造成的 也有服务商网络问题造成的 也有区域网络问题造成的 这个时候就需要用到ping traceroute mtr这三个命令 1 ping 最简单的网络请求反馈命令
  • 粒子群算法优化的最小二乘支持向量机分类代码

    粒子群算法优化的最小二乘支持向量机分类代码 在数据挖掘和机器学习领域中 分类是一个非常基础而重要的问题 其中最小二乘支持向量机 LSSVM 是一种有效的分类方法 经常被应用于实际问题中 而粒子群算法 PSO 是一种优化算法 也可以用来优化L
  • C++之函数模板

    1 什么是模板 模板有什么作用 模板分为函数模板和类模板 函数模板是对函数功能框架的描述 具体功能由实际传递的参数决定 有了函数模板 编译器就会根据模板自动生成多个函数名相同 参数列表不同的函数 不需要手动写 例 求一个矩形面积 当传入的长