[Effective C++]条款45 运用成员函数模板接受所有兼容类型
本文初发于 “天目中云的小站”,同步转载于此。
条款45 : 运用成员函数模板接受所有兼容类型
本条款中我们将会以智能指针为例, 介绍如何通过成员函数模板使一个模板类可以接受所有兼容类型.
我们先来构建一个简单的继承体系 :
class Top { ... };
class Middle: public Top { ... };
class Bottom: public Middle { ... };
Top *pt1 = new Middle; // Middle*隐式转换为Top*
Top *pt2 = new Bottom; // Bottom*隐式转换为Top*
const Top *pct2 = pt1; // Top*隐式转换为const Top*
在本例中, Top
是最初始的基类, 依次派生出Middle
和Bottom
, 通过隐式转换, 各种指针类型是可以合理地进行隐式转换的. 但当我们想用智能指针代行管理事务时, 再想这样的转换就比较麻烦了, 当然标准库中的智能指针已经解决了这种问题, 我们现在要讨论的就是标准库是如何实现智能指针之间的隐式转换的, 我们期待的效果如下 :
template<typename T>
class SmartPtr {
public: explicit SmartPtr(T *realPtr); // 通过资源指针进行初始化...
};SmartPtr<Top> pt1 = SmartPtr<Middle>(new Middle);
SmartPtr<Top> pt2 = SmartPtr<Bottom>(new Bottom);
SmartPtr<const Top> pct2 = pt1;
这段代码是无法通过编译的, 因为就算是Top
和Middle
有联系, SmartPtr<Top>
和SmartPtr<Middle>
也没有任何联系, 它们是无法隐式转换的, 但是我们可以通过成员函数模板, 具体说是写一个泛化copy构造函数来创造这种联系.
泛化copy构造函数
我们先来写一个成员函数模板中的泛化copy构造函数 :
template<typename T>
class SmartPtr {
public:template<typename U> // 泛化copy构造函数SmartPtr(const SmartPtr<U>& other); ...
};
这个模板函数接受用一个SmartPtr<U>
类型的参数去构造一个SmartPtr<T>
类型的对象, 现在还只是声明, 我们应该考虑如何定义内部逻辑, 正常逻辑应该是先看U*
是否可以隐式转换为T*
, 如果可以转换也就可以进行智能指针之间的转换, 我们来看代码 :
template<typename T>
class SmartPtr {
public:template<typename U>SmartPtr(const SmartPtr<U>& other) : heldPtr(other.get()) {...} // 关键代码T* get() const { return heldPtr; }...
private: T *heldPtr;
};
这里直接将参数other中的heldPtr
取出, 赋值给当前对象的heldPtr
. 其实在这里就进行了检查 :
other.get()
取出的指针类型为U*
, 如果U*
可以隐式转换为T*
, 那么可以进行智能指针之间的转换.- 如果不可以隐式转换, 编译错误, 会被系统拦截.
至此, 通过泛化copy构造函数这个成员函数模板, 只要U*
可以隐式转换为T*
, 那么SmartPtr<U>
也可以隐式转换为SmartPtr<T>
.
成员函数模板
成员函数模板的效用不只局限于构造函数, 也可以支持赋值操作, 其不改变语言规则, 但是可以帮助你让class在构造和赋值操作上可以兼容更多类型, 让我们在使用模板类型时可以像使用非模板类型时一样自然流畅. 我们可以了解一下标准库中shared_ptr
的简略版本, 看看其对成员函数模板的使用 :
template<class T>
class shared_ptr {
public:// 构造template<class Y> explicit shared_ptr(Y * p); // 泛化构造函数template<class Y> shared_ptr(shared_ptr<Y> const& r); // 泛化copy构造函数template<class Y> explicit shared_ptr(weak_ptr<Y> const& r); // 通过weak_ptr构造template<class Y>explicit shared_ptr(unique_ptr<Y>& r); // 通过unique_ptr构造// 赋值template<class Y> shared_ptr& operator=(shared_ptr<Y> const& r); // 泛化赋值重载template<class Y> shared_ptr& operator=(unique_ptr<Y>& r); // 用unique_ptr赋值...
};
在本例中, 构造函数中只有泛化copy构造函数没有explicit, 说明其他构造函数都不应当支持隐式类型转换, 也就是说T和Y的类型应当一致. 另外成员模板函数并不改变语言规则, 就算我们写了泛化的拷贝构造函数和赋值重载, 依旧不影响普通的拷贝构造和赋值重载, 如果我们没有写, 编译器还是会自动生成, 所以如果想要控制构造的方方面面, 我们应当同时声明普通版本和泛化版本.
template<class T>
class shared_ptr {
public:shared_ptr(shared_ptr const& r); // 普通拷贝构造template<class Y> shared_ptr(shared_ptr<Y> const& r); // 泛化拷贝构造shared_ptr& operator=(shared_ptr const& r); // 普通赋值重载template<class Y> shared_ptr& operator=(shared_ptr<Y> const& r); // 泛化赋值重载...
};
请记住 :
- 使用成员函数模板可以生成"可接受所有兼容类型"的函数.
- 当我们声明泛化拷贝构造和赋值重载时, 也应该声明其普通版本.
相关文章:
[Effective C++]条款45 运用成员函数模板接受所有兼容类型
本文初发于 “天目中云的小站”,同步转载于此。 条款45 : 运用成员函数模板接受所有兼容类型 本条款中我们将会以智能指针为例, 介绍如何通过成员函数模板使一个模板类可以接受所有兼容类型. 我们先来构建一个简单的继承体系 : class Top { ... }; class Middle: p…...

Harry技术添加存储(minio、aliyun oss)、短信sms(aliyun、模拟)、邮件发送等功能
Harry技术添加存储(minio、aliyun oss)、短信sms(aliyun、模拟)、邮件发送等功能 基于SpringBoot3Vue3前后端分离的Java快速开发框架 项目简介:基于 JDK 17、Spring Boot 3、Spring Security 6、JWT、Redis、Mybatis-P…...

【python基础——异常BUG】
什么是异常(BUG) 检测到错误,py编译器无法继续执行,反而出现错误提示 如果遇到错误能继续执行,那么就捕获(try) 1.得到异常:try的执行,try内只可以捕获一个异常 2.预案执行:except后面的语句 3.传入异常:except … as uestcprint(uestc) 4.没有异常:else… 5.鉴定完毕,收尾的语…...

解决Qt打印中文字符出现乱码
在 Windows 平台上,默认的控制台编码可能不是 UTF-8,这可能会导致中文字符的显示问题。 下面是在 Qt 应用程序中设置中文字体,并确保控制台输出为 UTF-8 编码: 1. Qt 应用程序代码 在 Qt 中,我们可以使用 QApplic…...

第三十八章 Spring之假如让你来写MVC——适配器篇
Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…...

服务器引导异常,Grub报错: error: ../../grub-core/fs/fshelp.c:258:file xxxx.img not found.
服务器引导异常,Grub报错: error: ../../grub-core/fs/fshelp.c:258:file xxxx.img not found. 1. 故障现象2. 解决思路3. 故障分析4. 案件回溯5. 解决问题 1. 故障现象 有一台服务器业务报无法连接. 尝试用Ping命令发现无法ping通. 通过控制台查看发现有以下报错: error: ..…...
昵称 校验
1. 基本格式校验 1. 长度限制 • 设置最小和最大字符长度:2-20 个字符(常见范围)。 • 避免昵称过短或过长影响显示和识别。 • 示例: • 2 ≤ 长度 ≤ 20:let minLength 2 let maxLength 20 if nickname.count <…...
MATLAB学习笔记目录
MATLAB学习笔记-生成纯音并保存-CSDN博客 MATLAB学习笔记-各种格式之间的转换 - 知乎 MATLAB学习笔记-胞组(cell array)转换为矩阵,cell2mat_matlab如何把元胞数组改为矩阵-CSDN博客MATLAB学习笔记-判断数组、结构体、数值、字符串是否相同…...

基于单片机的语音控制玩具汽车的设计
语音控制小汽车选用了两个单片机、一个语音识别芯片、两个无线收发模块、一个电机驱动模块、两个电机、一个音频解码模块。语音控制端选用了一个语音识别芯片,实现了将声音信号转换成数字信号,再将数据传输给单片机的功能。小车端选用了单片机来控制电机…...

Qt WORD/PDF(五)使用Json一键填充Word表格
关于QT Widget 其它文章请点击这里: QT Widget 国际站点 GitHub: https://github.com/chenchuhan 国内站点 Gitee : https://gitee.com/chuck_chee 姊妹篇: 《Qt WORD/PDF(一)使用 QtPdfium库实现 PDF 操作》 《Qt WORD/PDF&#…...
vue3+ts的几个bug调试
由于编译问题,把几个type检查给关闭了,否则错误太多。 1)第一个检查出的问题,拼写错误数组的length,写成了lengh。 2)数组的对象引用。 torStatus Array(8).fill({ ...defaultStatus }) as TorStatus[]…...

DVWA靶场CSRF漏洞通关教程及源码审计
目录标题 CSRFlow源码审计 medium源码审计 high源码审计 impossible源码审计 CSRF low 先修改密码 看到地址栏 复制在另一个网页打开 成功登录 源码审计 没有任何过滤措施,很危险,并且采用了不安全的md5加密 <?phpif( isset( $_GET[ Change ] )…...
前端开发:HTML常见标签
1.注释标签 注释不会显示在界面上 . 目的是提高代码的可读性 . ctrl / 快捷键可以快速进行注释 / 取消注释 . <!-- 我是注释 --> 2.标题标签 有六个 , 从 h1 - h6. 数字越大 , 则字体越小 <h1> hello </h1> //我们所写的csdn的格式中的标题一…...
【机器学习】主动学习-增加标签的操作方法-样本池采样(Pool-Based Sampling)
Pool-Based Sampling Pool-based sampling 是一种主动学习(Active Learning)方法,与流式选择性采样不同,它假设有一个预先定义的未标注样本池,算法从中选择最有价值的样本进行标注,以提升模型的性能。这种…...

【Rust自学】11.9. 单元测试
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 11.9.1. 测试的分类 Rust把测试分为两类,一个是单元测试,一个是集成测试。 单元测试比较小也比较专注ÿ…...
深入理解Web存储机制:Cookie、SessionStorage与LocalStorage的区别
文章目录 前言一、Cookie简介二、SessionStorage简介三、LocalStorage简介四、三者之间的比较五、最佳实践建议结语 前言 随着Web应用程序变得越来越复杂,开发者需要更有效的办法来管理客户端数据。Cookie、SessionStorage和LocalStorage是三种常用的Web存储机制&a…...
SpringBoot之BeanDefinitionLoader类源码学习
该类的作用 Spring 框架中用于加载和解析 Bean 定义的工具类。它主要用于从不同的资源(如 XML 文件、注解、Java 配置类等)中读取 Bean 定义,并将这些定义注册到 Spring 的 BeanFactory 或 ApplicationContext 中 基本属性 //指定的资源pri…...

【芯片封测学习专栏 -- 2D | 2.5D | 3D 封装的区别和联系】
请阅读【嵌入式开发学习必备专栏 Cache | MMU | AMBA BUS | CoreSight | Trace32 | CoreLink | ARM GCC | CSH】 文章目录 Overview线键合(wire-bonding)封装FOWLP2D封装2.5D 封装硅通孔(TSV)硅中介层无TSV的2.5D 3D封装 Overview 我们先要了解一下&…...

从硬件设备看Linux
一、介绍 DM3730通过各种连接方式连接了各种设备,输入输出设备根据不同的类型大体可 以分为电源管理、用户输人、显示输出、图像采集、存储以及无线设备等。我们可以将DM 3730与这些设备的数据接口分为总线和单一的数据接口总线。总线的显著特点是单个总线上可以连…...

open3d+opencv实现矩形框裁剪点云操作(C++)
👑主页:吾名招财 👓简介:工科学硕,研究方向机器视觉,爱好较广泛… 💫签名:面朝大海,春暖花开! open3dopencv实现矩形框裁剪点云操作(Cÿ…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...

sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...