C++学习 十、函数重载,函数模板

2023-11-16

前言

本篇继续C++记录,函数重载与函数模板。

函数重载

函数重载是C++的特色,允许一个程序中定义多个同名函数,但函数的参数列表(也称为特征标)必须不同。可以通过函数重载,使函数根据不同类型的参数实现不同的功能(相同的也行),示例如下:

void swap(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

void swap(double& a, double& b) {
	double temp = a;
	a = b;
	b = temp;
}

void main(){
	int x=1, y=2;
	swap(x, y);
}

编译器会按照调用函数的参数个数和类型,决定使用哪个同名函数。

参数类型和个数完全匹配的同名函数将被优先使用;如果参数类型没有完全匹配,则查找能够通过强制类型转换达成要求的同名函数。否则会报找不到函数的错误。

注意:函数重载必须是参数列表不同,函数返回类型不能作为重载依据。编译只会根据函数名和参数列表,对函数名进行区分。

二义性

函数重载容易出现的问题就是二义性,即编译器不能根据参数区分同名函数。下面是几种常见的情况。

强制类型转换

如果实参与参数列表不完全匹配,编译器在强制类型转换时,容易出现二义性,如下所示:

int func()(int a);
long func()(long b);

float r=1.;func(r);将报错error: call of overloaded 'func(double)' is ambiguous,这就是强制类型转换带来的二义性。

在同名函数选择中,强制类型转换也具有优先级,但这与编译器相关。一般而言,短整形转长整形,单精度转双精度浮点型会优于整形转浮点型,长整型转短整型,双精度转单精度浮点型。

比如下面的重载就不会报错:

int func(double a);
int func(int a);
void main(){
	float r;
	func(r);
}

而这样的重载会报错:

int func(double a);
int func(int a);
void main(){
	long r;
	func(r);
}

类型与类型引用

C++将类型与类型引用看作相同的特征标,如下所示:

void func(int a);
void func(int& a);

上面的func参数列表看似不同,然而int x=1;func(x);,两个同名函数都满足参数要求,出现了二义性。

默认参数

如果两个同名函数的参数个数不同,参数多的函数带有默认参数,如下所示:

void func(int a, int b, int c=5);
void func(int a, int b);

void main(){
	int x=1,y=2;
	func(a, b);
}

则会出现二义性错误,编译器不知道应该使用哪个同名函数。

const指针参数和const引用参数

对于指针和引用参数而言
普通函数中,const实参不能传给非const形参,而非const实参,可以传给const形参(原因在下篇const关键字中会提到)。

重载函数中,非const实参将优先匹配非const形参函数,而const实参当然只能匹配const实参函数了。

注意:对于非指针和引用类型,下面的定义直接报redefinition重定义错误:

void func(int a);
void func(const int a);

函数模板

函数模板也是C++的一大特色,泛型编程。如果一个函数对于多种类型都有相同的功能,那么使用函数模板能够节省时间,提升效率。

比如交换两个变量,如果使用函数重载,那么对于整型,浮点型,字符,字符串,数组等都需要写一个同名函数,定义类似:

void swap(int& a, int& b){
	int temp = a;
	a = b;
	b = temp;
}
void swap(double& a, double& b){
	double temp = a;
	a = b;
	b = temp;
}
...

函数模板则可以使用任何类型来定义函数:

template<typename T>
void swap(T& a, T& b){
	T temp = a;
	a = b;
	b = temp;
}

void main(){
	int x=1,y=2;
	swap(x, y);
}

template<typename T>指出要建立一个模板,类型名为T,然后定义了模板函数。此外,参数列表并不要求都是类型T

模板并不会创建函数,而只是告诉编译器怎样定义函数。随后,编译器在程序中查找调用swap模板的参数类型,根据模板生成相应类型的函数,这种操作叫做隐式实例化implicit instantiation。上面的程序中,编译器创建了int类型的swap函数。

最终生成的代码中并不带有模板函数,而是包含实际创建的函数。

模板函数声明与定义

在我之前的博客中记录了定义模板时的一个问题:把模板声明与定义放在.h和.cpp文件中,如果在其它源文件调用模板.h文件,会报undefined reference的问题。

原因:由于隐式实例化,在模板的.cpp定义文件中,函数模板没有被调用,因此编译模板的.cpp定义文件时,不会创建真实的函数;而其它源文件中没有模板的定义文件,即使包含了模板.h声明文件,也不会创建真实的函数。因此,编译器就找不到这个函数的定义了。

解决方法一:把模板声明和定义都放在.h文件中,虽然这并不符合C++编程“美学”。
解决方法二:使用后面将提到的显式实例化。

函数模板重载

函数模板也可以被重载,并也要求参数列表不同。

注意:在常规函数、重载函数、函数模板共存的情况下,如果调用实参与常规函数或者重载函数匹配,则不会调用模板创建函数。

显式实例化

隐式实例化导致模板声明.h,定义.cpp文件分离出错,可以通过显式实例化直接在.cpp文件中创建函数:

template 
void swap<int>(int&, int&);

编译器看到上面的声明后,就根据函数模板创建一个int类型的swap函数实例。

显式具体化

上面的函数模板swap可以交换很多类型,比如整型,浮点型。但是,有的时候模板并不能完全适用于所有类型。比如我想交换结构myStruct的成员c,但其它成员保持不变,如下所示:

struct myStruct{
	int a;
	float b;
	double c;
};
void main(){
	myStruct t1{1,2,3}, t2{4,5,6};
	swap(t1.c, t2.c);
}

当然可以采用上面的方案,以结构的成员作为swap的参数。

然而,如果我想直接以结构作为参数,那么就需要采用模板具体化,针对某个类型提供具体的声明:

template <> void swap<myStruct>(myStruct& s1, myStruct& s2);

或者

template <> void swap(myStruct& s1, myStruct& s2); //equal

然后定义:

template <> 
void swap<myStruct>(myStruct& s1, myStruct& s2) {
	myStruct temp = s1;
	s1.c = s2.c;
	s2.c = temp.c;
}

乍一看,模板显式具体化与重载很像,都是给函数一个不同的实现方式。但是显式具体化是为模板的某种类型提供特殊的实现方法,由具体化类型(本例中是myStruct)代替了T;重载则与T无关。

显式实例化与显式具体化的区别在于,显式实例化仍然是根据原模板函数,创建指定类型的实例;而显式具体化则是为指定类型提供了不同的定义。

注意:同一种类型的显式具体化与显式实例化不能同时存在,否则会报错。

隐式实例化、显式实例化、显式具体化统称为具体化,它们都创建了函数实例。而函数模板只是告诉编译器该怎样定义函数。

重载解析

由于函数重载、函数模板、函数模板重载的共存,C++需要判断使用哪一个函数,称为重载解析。

重载解析的过程是:

  1. 挑选与调用函数同名的函数重载,函数模板和模板重载,形成候选函数列表;
  2. 从候选函数列表中,挑选参数列表匹配的候选函数,形成匹配函数列表;
  3. 从匹配函数列表中,挑选最佳函数。

最佳函数的判定顺序如下:

  1. 参数类型完全匹配,但常规函数优于模板;
  2. 提升转换,如短整型变长整型,单精度变双精度等;
  3. 标准转换,如整型变浮点型等;
  4. 其它转换。

此外,除了类型完全一致外,C++认为以下类型也属于完全匹配:

  • TypeName与TypeName&
  • TypeName[]与TypeName*
  • TypeName(实参)-> const or volatile TypeName(形参)
  • TypeName*(实参)-> const or volatile TypeName*(形参)

完全匹配的非模板函数优于模板生成的函数。完全匹配的函数模板,显式具体化优于实例化。

引导编译器使用函数模板

有时,可以指示编译器使用函数模板而不是常规函数:

void main(){
	int x=1, y=2;
	swap<>(x, y);
}

上面的语句中,swap<>(x, y)明确指出使用函数模板创建函数实例。

另外,还可以要求编译器进行显式实例化:

void main(){
	int x=1, y=2;
	swap<int>(x, y);
}

上面的语句中,swap<int>(x, y)明确要求进行int的显式实例化。

后记

本篇比较详细的记录了C++函数重载和函数模板的内容。下篇将记录C++ const关键字与volatile关键字。

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

C++学习 十、函数重载,函数模板 的相关文章

  • 井字游戏代码有助于改进

    这是我必须检查玩家在井字棋游戏中获胜的代码 这是一个很长的 if 语句 可以改进 该板由 9 个图片框组成 我是一名 C 初学者 pBox Image Player players Player playerTurn getImage ch
  • C++ 天花板函数的奇怪结果

    我一直在尝试天花板功能并得到一些奇怪的结果 如果我对十进制数乘以百执行 ceil 运算 我会得到一定的结果 但是 如果我直接对该乘法的结果执行 ceil 我会得到完全不同的输出 另一个问题是 这些不同的结果仅发生在某些数字上 任何帮助 将不
  • OpenGL,如何独立旋转对象?

    到目前为止我的代码 void display void glClear GL COLOR BUFFER BIT GL DEPTH BUFFER BIT Clear Screen And Depth Buffer glLoadIdentity
  • 以概率从列表中选择随机元素

    我有一个包含四个项目 A B C D 的列表 每个项目都有被选择的概率 例如 A 有 74 的机会被选中 B 15 C 7 D 4 我想创建一个函数 根据其概率随机选择一个项目 有什么帮助吗 为您的项目定义一个类 如下所示 class It
  • 运行 C# exe 文件

    复制 为什么我的 NET 应用程序在从网络驱动器运行时会崩溃 https stackoverflow com questions 148879 why does my net application crash when run from
  • 用C#发送USSD?

    我想编写一个在 Windows Mobile 6 上运行的简单 C 应用程序 它可以发送 USSD 消息 有没有任何图书馆可以帮助我做到这一点 或者是否有任何示例解释如何使用线路发送USSD http msdn microsoft com
  • 更新 OSX 命令行工具 6.3 后缺少 C++ 标头 <__debug>

    从 App Store 更新到 Command Line Tools 6 3 后 程序包括
  • 如何在C中递归地找到另一个字符串中的字符串位置?

    我们有一个任务来创建带有两个字符串参数的递归函数 原型应该是这样的 int instring char word char sentence 如果我们愿意调用函数 instring Word Another Word 它应该具有以下返回值
  • C语言中的array、&array、&array[0]有什么区别? [复制]

    这个问题在这里已经有答案了 在学习C语言中的数组和指针时 我很困惑 为什么ch ch ch 0 彼此相等 而sptr sptr sptr 0 却不相等 这是我的源代码 int main void char ch 7 1 2 3 4 5 6
  • Bool类型返回规则

    我使用 dapper ORM 所以我使用两个规则Query
  • 为什么 C++ 元组如此奇怪?

    我通常创建自定义structs将不同类型的值分组在一起时 这通常很好 而且我个人发现命名成员访问更容易阅读 但我想创建一个更通用的 API 在其他语言中广泛使用元组后 我想返回类型的值std tuple但发现它们在 C 中使用比在其他语言中
  • 使用可变参数模板函数计算多个值的平均值

    我正在尝试编写一个函数来确定任意数量参数的平均值 所有参数都具有相同的类型 出于学习目的 我尝试使用可变参数模板函数来做到这一点 这是我到目前为止所拥有的 template
  • 将 .NET 类库(主要定义 CRUD 操作)公开为服务

    公开现有内容的最佳 有效和最快的方法是什么 类 图书馆 主要定义 CRUD 操作 作为service 周转基金服务 or WCF数据服务 以便它可以与银光 or Ajax 在那儿tools 代码生成器 RAD 工具 哪些可以支持这个 预先感
  • 如何将这个基于代码的 WPF 工具提示转换为 Silverlight?

    以下工具提示代码适用于WPF 我正在努力让它发挥作用银光 但它给了我这些errors TextBlock does not contain a definition for ToolTip Cursors does not contain
  • 在运行时将项目添加到 ToolStrip

    您好 我有一个带有 收藏夹 菜单的 ToolStripMenu 我想在运行时在 WinForms 应用程序中添加子项目 我有一个 datagridview 右键单击它会显示一个包含 添加到收藏夹 选项的上下文菜单 当该事件被触发时 我想使用
  • 在 Visual Studio C++ 资源编辑器中导入 png 文件

    我希望能够在 Visual Studio 资源编辑器中导入 png 文件 以便能够在不同的其他项目中使用嵌入的资源 有解决办法吗 我知道它适用于位图 但我对 png 感兴趣 因为即使在较低格式 16x16 或 32x32 上也可以使用 透明
  • 返回右值 - 这段代码有什么问题? [复制]

    这个问题在这里已经有答案了 我遇到了以下代码片段 std string test std string m Hello return std move m int main std string m test 我知道上面的代码是不正确且不安
  • 当前线程中的单例

    我的单身人士如下 public class CurrentSingleton private static CurrentSingleton uniqueInstance null private static object syncRoo
  • “while(true) { Thread.Sleep }”的原因是什么?

    我有时会遇到以下形式的代码 while true do something Thread Sleep 1000 我想知道这是否被认为是好的做法还是坏的做法以及是否有任何替代方案 通常我在服务的主函数中 找到 这样的代码 我最近在 Windo
  • 我应该为每个 Web 请求使用静态缓存的 ResourceManager 还是一个新实例?有关系吗?

    创建新的 NET 对性能 或其他 有何影响 如果有 ResourceManager根据每个请求new ResourceManger myResourceType FullName myResourceType Assembly 与在 Des

随机推荐

  • 教你怎么在linux上永久修改IP地址

    经常移动电脑而使电脑ip改变 有两种方式 在创建虚拟机时使用NAT模式和桥接模式 使用NAT模式 就是虚拟机ip所依赖主机的ip而产生 使用桥接模式 不依赖主机ip而产生ip 相当于在当前网络中有一个电脑占据了这个ip 使用NAT模式 本身
  • HttpMediaTypeNotAcceptableException的解决过程

    今儿的Web项目中突然报错 HttpMediaTypeNotAcceptableException Could not find acceptable representation 涉及接口是 RequestMapping value X
  • JAVA语言特点

    Java语言特点 Java语言是面向对象的 oop Java语言是健壮的 Java的强类型机制 异常处理 垃圾的自动收集等是Java程序健壮性的重要保障 Java语言是跨平台性的 即一个编译好的 class文件可以在多个系统下运行 借助各系
  • 创建Vue3.0工程和常用 Composition API

    一 创建Vue3 0工程 1 使用 vue cli 创建 官方文档 https cli vuejs org zh guide creating a project html vue create 查看 vue cli版本 确保 vue cl
  • QWidget关闭子窗口后显示主窗口(父窗口)

    已经弃坑 不再使用Qt工具 有疑问可以查阅博主的其它博客 看看有没有解决方案 如果还没有可以百度或Google搜一下其他博主的教程 谢谢 Holle 好久没见了 据我上次发博客已经过去很久 今天呢 带给大家一个Qt上的小问题 好多初学者在Q
  • 回顾篇-SpringBoot-Tomcat

    为什么写 今天看了Spring实战第五版 里边有句话如下 传统的基于Servlet的Web框架 如Spring MVC 在本质上都是阻塞和多线程的 每个连接都会使用一个线程 在请求处理的时候 会在线程池中拉取一个工作者 worker 线程来
  • Linux下修改密码命令及查看密码修改时间

    Linux下修改密码命令及查看密码修改时间 1 如修改用户liu的密码为123456 在root用户下执行 echo 123456 passwd stdin liu 2 查看密码修改时间 date d 1970 01 01 more etc
  • CloudCompare——计算点云的KD树并可视化

    目录 1 功能概述 2 完整操作 3 算法源码 4 相关代码 1 功能概述 使用Tools gt Sand box research gt compute kd tree访问此工具 该功能可以用于论文写作中的KD树绘图 2 完整操作 3 算
  • 你的小程序

    你的小程序还未设置管理员信息 无法被绑定 你可先访问mp weixin qq com 在 用户身份 页面设置管理员信息后即可进行绑定 解决方法 换一个浏览器 别用火狐 真XXX 但解决放哪就这么简单
  • 如何在Visual Studio给多行代码进行注释和取消注释

    如何在Visual Studio给多行代码进行注释和取消注释 用鼠标选中要注释的代码 先按Ctr K 键 再按 Ctrl C 键进行注释多行代码 如果要取消注释 用鼠标选中被注释的代码 先按Ctrl K 再按 Ctrl U 即可取消注释
  • 中文OCR识别

    在闲暇时刻做了一个中文识别能力的工程 工程主要对中文文字进行识别 当前工程没有检测文本能力 后续会加入 文本字段在32000验证集合上准确率为83 2 可识别中文字符5990 由于没有时间做太多优化 可能对于相近字体的准确率并不太高 但是可
  • 拿来即用的 Python SSH+SFTP 实现类

    一个拿来即用的 Python SSH 和 SFTP 实现类 可用于 与 Linux 服务器建立持续交互的 SSH 会话 从 Linux 服务器下载远程文件 上传本地文件到 Linux 服务器 新创建一个 linux client py 文件
  • Vue.js中的v-model指令(双向绑定)

    Vue js中v model的作用 v model的作用和使用场景 1 v model的作用 双向绑定 2 v model双向绑定的使用场景 表单 3 总结 v model的作用和使用场景 你好 Vue js作为现在最为常用的前端框架之一
  • UI自动化测试通过飞书发送告警信息

    1 发送纯文本消息 1 1代码如下 usr bin env python coding utf 8 import json import requests url https open feishu cn open apis bot v2
  • Matting(抠图)--用深度学习自动去除照片背景

    转自 https zhuanlan zhihu com p 38031181 https zhuanlan zhihu com p 151212267 现在又有一个 AI 能干 Photoshop 的活了 自动抠图 一键去除照片背景 这款
  • AD如何画多图纸原理图

    很多东西都讲究模块化 比如 程序模块化 原理图也不例外 模块化后的原理图更能直观的显示其原理 文章中操作的图标是什么功能可以先看下参考文献中的博客在来看看我的这篇博客 效果会更好 原本以为原理图多图纸很难 其实也差不多 只是多了几步罢了 多
  • getClassLoader()返回null,getClassLoader()获取为空

    一 问题描述 通过getClassLoader 的方式获取jar包中的资源 为空 this getClassLoader getResources com xxxx test Start class hasMoreElements this
  • gin context和官方context_go-gin框架入门

    gin入门 介绍 gin是一个golang的微框架 封装比较优雅 api友好 源码注释比较明确 具有快速灵活 容错方便等特点 对于golang而言 web框架的依赖要比python java之类的要小的多 完全使用自身net http包封装
  • 课时 10 自测题

    使用存储快照功能需要用到哪些 Kubernetes API 资源对象 多选题 A VolumeSnapshot B VolumeSnapshotClass C VolumeSnapshotContent D PersistentVolume
  • C++学习 十、函数重载,函数模板

    C 学习 十 函数重载 函数模板 前言 函数重载 二义性 强制类型转换 类型与类型引用 默认参数 const指针参数和const引用参数 函数模板 模板函数声明与定义 函数模板重载 显式实例化 显式具体化 重载解析 引导编译器使用函数模板