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 中可用的所有编译时运算符吗 C 中有两个运算符 无论操作数如何 它们的结果始终可以在编译时确定 它们是sizeof 1 and 2 当然 其他运算符的许多特殊用途可以在编译时解决 例如标准中列出的那些整数常量表达式 1 与
  • 如何使用 C# 中的参数将用户重定向到 paypal

    如果我有像下面这样的简单表格 我可以用它来将用户重定向到 PayPal 以完成付款
  • “构建”构建我的项目,“构建解决方案”则不构建

    我刚刚开始使用VS2010 我有一个较大的解决方案 已从 VS2008 成功迁移 我已将一个名为 Test 的控制台应用程序项目添加到解决方案中 选择构建 gt 构建解决方案不编译新项目 选择构建 gt 构建测试确实构建了项目 在失败的情况
  • 为什么 C# Array.BinarySearch 这么快?

    我已经实施了一个很简单用于在整数数组中查找整数的 C 中的 binarySearch 实现 二分查找 static int binarySearch int arr int i int low 0 high arr Length 1 mid
  • WCF RIA 服务 - 加载多个实体

    我正在寻找一种模式来解决以下问题 我认为这很常见 我正在使用 WCF RIA 服务在初始加载时将多个实体返回给客户端 我希望两个实体异步加载 以免锁定 UI 并且我想利用 RIA 服务来执行此操作 我的解决方案如下 似乎有效 这种方法会遇到
  • 在哪里可以找到列出 SSE 内在函数操作的官方参考资料?

    是否有官方参考列出了 GCC 的 SSE 内部函数的操作 即 头文件中的函数 除了 Intel 的 vol 2 PDF 手册外 还有一个在线内在指南 https www intel com content www us en docs in
  • ASP.NET MVC:这个业务逻辑应该放在哪里?

    我正在开发我的第一个真正的 MVC 应用程序 并尝试遵循一般的 OOP 最佳实践 我正在将控制器中的一些简单业务逻辑重构到我的域模型中 我最近一直在阅读一些内容 很明显我应该将逻辑放在域模型实体类中的某个位置 以避免出现 贫血域模型 反模式
  • 使用实体框架模型输入安全密钥

    这是我今天的完美想法 Entity Framework 中的强类型 ID 动机 比较 ModelTypeA ID 和 ModelTypeB ID 总是 至少几乎 错误 为什么编译时不处理它 如果您使用每个请求示例 DbContext 那么很
  • 从Web API同步调用外部api

    我需要从我的 Web API 2 控制器调用外部 api 类似于此处的要求 使用 HttpClient 从 Web API 操作调用外部 HTTP 服务 https stackoverflow com questions 13222998
  • HTTPWebResponse 响应字符串被截断

    应用程序正在与 REST 服务通信 Fiddler 显示作为 Apps 响应传入的完整良好 XML 响应 该应用程序位于法属波利尼西亚 在新西兰也有一个相同的副本 因此主要嫌疑人似乎在编码 但我们已经检查过 但空手而归 查看流读取器的输出字
  • 带动态元素的 WPF 启动屏幕。如何?

    我是 WPF 新手 我需要一些帮助 我有一个加载缓慢的 WPF 应用程序 因此我显示启动屏幕作为权宜之计 但是 我希望能够在每次运行时更改屏幕 并在文本区域中显示不同的引言 这是一个生产力应用程序 所以我将使用非愚蠢但激励性的引言 当然 如
  • WCF 中 SOAP 消息的数字签名

    我在 4 0 中有一个 WCF 服务 我需要向 SOAP 响应添加数字签名 我不太确定实际上应该如何完成 我相信响应应该类似于下面的链接中显示的内容 https spaces internet2 edu display ISWG Signe
  • 使用 Bearer Token 访问 IdentityServer4 上受保护的 API

    我试图寻找此问题的解决方案 但尚未找到正确的搜索文本 我的问题是 如何配置我的 IdentityServer 以便它也可以接受 授权带有 BearerTokens 的 Api 请求 我已经配置并运行了 IdentityServer4 我还在
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 如何在整个 ASP .NET MVC 应用程序中需要授权

    我创建的应用程序中 除了启用登录的操作之外的每个操作都应该超出未登录用户的限制 我应该添加 Authorize 每个班级标题前的注释 像这儿 namespace WebApplication2 Controllers Authorize p
  • 什么时候虚拟继承是一个好的设计? [复制]

    这个问题在这里已经有答案了 EDIT3 请务必在回答之前清楚地了解我要问的内容 有 EDIT2 和很多评论 有 或曾经 有很多答案清楚地表明了对问题的误解 我知道这也是我的错 对此感到抱歉 嗨 我查看了有关虚拟继承的问题 class B p
  • Windows 窗体:如果文本太长,请添加新行到标签

    我正在使用 C 有时 从网络服务返回的文本 我在标签中显示 太长 并且会在表单边缘被截断 如果标签不适合表单 是否有一种简单的方法可以在标签中添加换行符 Thanks 如果您将标签设置为autosize 它会随着您输入的任何文本自动增长 为
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • 如何防止用户控件表单在 C# 中处理键盘输入(箭头键)

    我的用户控件包含其他可以选择的控件 我想实现使用箭头键导航子控件的方法 问题是家长控制拦截箭头键并使用它来滚动其视图什么是我想避免的事情 我想自己解决控制内容的导航问题 我如何控制由箭头键引起的标准行为 提前致谢 MTH 这通常是通过重写

随机推荐

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