【模板的特殊继承关系】 奇异的递归模板模式
一、奇异的递归模板模式范例
奇异的递归模板模式 ( C u r i o u s l y R e c u r r i n g T e m p l a t e P a t t e r n ) (Curiously \ Recurring \ Template \ Pattern) (Curiously Recurring Template Pattern)不是一种新技术,而是一种模板编程中使用的编程手法:把派生类作为基类的模板参数。
下面是一个简单范例:
//基类
template<typename T>
class Base {//...
};//派生类1
class Derived1 : public Base<Derived1> { //派生类继承基类的派生类参数的泛型
public:void myfunc() {std::cout << "Derived1::myfunc()执行了!\n";}};//派生类2(模板)
template<typename T>
class Derived2 : public Base<Derived2<T>> {};
这样的继承方式在模板与泛型编程中很常见。
以下介绍一下基本的几个用法。
二、在基类中使用派生类对象
一般情况下,我们可以通过 s t d : : s t a t i c _ c a s t < T > std::static\_cast<T> std::static_cast<T>静态类型转换将基类对象转换为派生类对象,从而在基类对象内使用派生类对象的接口。
如下代码所示:
template<typename T>
class Base {private:Base() {}friend T; //只有在类内实例化Base<T>的T是友元public:void asDerived() {T& derived = static_cast<T&>(*this); //将基类转为派生类derived.myfunc();}
};//派生类1
class Derived1 : public Base<Derived1> { //派生类继承基类的派生类参数的泛型
public:void myfunc() {std::cout << "Derived1::myfunc()执行了!\n";}};
为了防止继承时出错,我们在基类内增加一个友元类 T T T,只对相同派生类的类模板开放构造函数。
使用友元可以避免下面的错误:

相应的友元模板知识点可以参考:友元类模板,这里就不赘述了。
注意这里的写法:

当然,这个派生类也可以是一个模板:
//派生类2(模板)
template<typename T>
class Derived2 : public Base<Derived2<T>> {
public:void myfunc() {std::cout << "Derived2<T>::myfunc()执行了!\n";}
};
同时,我们在基类增加友元声明:
template<typename U>
friend class Derived2; // 允许所有 Derived2 的实例成为友元
这样我们实例化出的派生类都支持基类的私有访问了,调用下方测试代码:
void Test1() {Derived1 myd1;myd1.asDerived();Derived2<int> myd2;myd2.asDerived();
}
这样的运行结果是,我们在基类调用了派生类的方法,也就是可以看作是派生类为基类提供了接口,这与一般的继承调用不同(基类给派生类提供接口)
运行结果如下:

三、基于减少派生类代码量的考虑
通常,如果我们需要在派生类内增加功能,我们需要不断的补充代码,而这里的做法是,我们把一部分代码放入基类实现,让派生类内的代码相对简洁一些。
下面是一个例子:
//基于减少派生类中代码量的考虑template<typename T>
struct shape {//友元实现运算符重载friend bool operator==(const shape<T>& obj1, const shape<T>& obj2) {const T& objtmp1 = static_cast<const T&>(obj1);const T& objtmp2 = static_cast<const T&>(obj2);if (!(objtmp1 < objtmp2) && !(objtmp2 < objtmp1)) return true;return false;}
};struct square :public shape<square> {int sidelength;square(int length) :sidelength(length) {}};//全局实现运算符重载
bool operator<(square const& obj1, square const& obj2) {return obj1.sidelength < obj2.sidelength;
}
我们需要在 s q u a r e square square派生类里面增加一个运算符重载,我们可以把这一个操作放入基类中来实现,或者放入全局来实现。
这里详细说一下在基类实现:
1.我们需要将运算符设置为友元的,这样我们就能在派生类内访问到基类的运算符了。这样设置的友元是全局的,这在之前的友元章节有详细说明。
2.然后我们需要使用 s t d : : s t a t i c _ c a s t < T > std::static\_cast<T> std::static_cast<T>对传入的基类对象转为相应的派生类对象,最后进行比较即可。
调用测试代码:
void Test2() {square obj1(15), obj2(20), obj3(20);if (obj1 == obj2) {std::cout << "obj1 和obj2相等\n";}else {std::cout << "obj1 和obj2不相等\n";}if (obj2 == obj3) {std::cout << "obj2 和obj3相等\n";}else {std::cout << "obj2 和obj3不相等\n";}
这样,我们在派生类中无需添加任何代码,而将代码加入基类或全局。

三、基类调用派生的接口与多态的体现
就像上面说的,这样的奇异递归模板模式可以让基类调用派生类的接口。
下面我们看看具体的实现:
//基类
template<typename T>
class Human {
private:Human() {}friend T;public:T& toChild() {return static_cast<T&>(*this); //基类转派生类}void parenteat() {toChild().eat();}};//派生类1
class Men :public Human<Men> {
public:void eat() {std::cout << "男人喜欢吃面食!\n";}
};//派生类2
class Women :public Human<Women> {
public:void eat() {std::cout << "女人喜欢吃米饭!\n";}
};
在基类中,核心代码如下:

首先是转为派生类,然后调用派生类接口
Men mymen;
Women mywomen;//派生类给基类提供接口
mymen.parenteat();
mywomen.parenteat();
运行后我们得到:

这里实现了让基类调用我们派生类的接口,或者说是让派生类给基类提供接口。
我们也能在此基础上,添加一个函数模板:
template<typename T>
void myHumanFuncTest(Human<T>& tmpobj) {tmpobj.toChild().eat();
}
调用:
Men mymen;
Women mywomen;
myHumanFuncTest(mymen);
myHumanFuncTest(mywomen);
得到相同的结果:

实际上,可以发现,这是多态的体现,也是前面我们说过的静态多态。
这样的编程方法可以让我们更灵活:比如当 e a t ( ) eat() eat()函数是一个静态成员函数或者函数模板的时候,我们就无法使用虚函数进行动态多态,因此就必须使用奇异的递归模板模式了。
相关文章:
【模板的特殊继承关系】 奇异的递归模板模式
一、奇异的递归模板模式范例 奇异的递归模板模式 ( C u r i o u s l y R e c u r r i n g T e m p l a t e P a t t e r n ) (Curiously \ Recurring \ Template \ Pattern) (Curiously Recurring Template Pattern)不是一种新技术,而是一种模板编程中使用的编程手…...
SAP B1 单据页面自定义 - 用户界面编辑字段
背景 接《SAP B1 基础实操 - 用户定义字段 (UDF)》,在设置完自定义字段后,如下图,通过打开【用户定义字段】可打开表单右侧的自定义字段页。然而再开打一页附加页面操作繁复,若是客户常用的定义字段,也可以把这些用户…...
MinIO【部署 02】Linux集群版本及Windows单机版、单机多目录版、分布式版(cmd启动脚本及winsw脚本分享)
Linux集群版及Windows单机版分布式版 1.Linux集群版1.1 安装启动停止1.2 将MinIO添加到服务 2.Windows2.1 官网安装2.2 本地测试2.2.1 cmd启动脚本2.2.2 winsw脚本 3.总结 1.Linux集群版 官网下载地址 https://min.io/download#/linux; 官网安装文档 https://min.i…...
手握18个大厂offer,我在大模型风口起飞
前言 在“金三银四”这一招聘旺季中,社交媒体上满是分享 offer 信息的“求助帖”。这些帖子通常只公布公司名称与薪资区间,而将具体岗位模糊化,以此作为判断岗位是否值得入职的衡量标准。 2024 年毕业的 985 硕士白丁(化名&…...
邦芒忠告:办公室聊天应避开的四个话题
职场人生风云变幻,害人之心不可有,防人之心不可无。千万别把同事当知己,无话不谈,把自己的私域圈起来当成办公室话题的禁区,轻易不让人涉足,其实是非常明智的一招,是竞争压力下的自我保护。 话题…...
交易型开放式指数基金(ETF)
交易型开放式指数基金(Exchange Traded Fund,简称 ETF)是一种投资工具,以下是用通俗易懂的语言对其进行的讲解: 一、基本概念 想象 ETF 是一个大篮子,里面装着很多不同的东西。在金融市场里,这…...
opencv将灰度图转为彩色图片
文章目录 背景灰度图优势opencv读取灰度图彩色转灰度算法需求 方法测试代码 背景 在图像处理中通常需要将图片转为灰度图 灰度图,也称为灰度图像或黑白图像,是一种只包含亮度信息而不包含颜色信息的图像。在灰度图中,每个像素的亮度级别通常…...
判断PDF与图片是否可以预览
一、判断图片是否可以预览 在JavaScript中,可以使用Image对象来判断一个图片URL是否可以访问。如果图片可以被加载,那么load事件会被触发;如果图片无法访问,error事件会被触发。 function checkImageAccessibility(url, callbac…...
多线程与并发区别
在Java中,多线程与并发是两个既相关又有所区别的概念。我们可以这样来理解它们: 多线程(Multi-threading): 多线程是指程序能够同时执行多个线程。每个线程都是一个独立的执行流,它们共享程序的内存空间&a…...
这个桌面日历真不错 笔记 提醒 生日记录 打卡 翻译都有 真的太方便了!
这个桌面日历真不错 笔记 提醒 生日记录 打卡 翻译都有 真的太方便了!日历产品非常的多,如何选择一个合适自己的桌面日历,这个很重要,今天小编给大家介绍这个芝麻日历,一起看下它有些什么功能,是不是你需要…...
多模态大语言模型综述(中)-算法实用指南
本文是Multimodal Large Language Models: A Survey的译文之算法实用指南部分。 上:摘要、概念与技术要点实用指南中:算法实用指南(本文)下: 任务的实用指南(应用)、挑战等 原始信息 标题: Multimodal Large Language Models: A Survey译文: 多模态大…...
Qt | ubuntu20.04安装Qt6.5.3并创建一个example完整教程(涉及诸多开发细节,商用慎重)
点击上方"蓝字"关注我们 01、下载 >>> 下载Qt在线安装包 这里采用镜像地址进行下载,避免网络过慢。 镜像地址:http://mirrors.ustc.edu.cn/qtproject/archive/online_installers/4.5/ 选择最新版本下载,如截至目前最新版本为qt-unified-linux-x64-4.5.2…...
苏州科技大学、和数联合获得国家知识产权局颁发的3项发明专利证书
近日,基于“苏州科技大学-和数智能软件区块链技术工程实验室”的研究成果,国家知识产权局正式授权了苏州科技大学、苏州和数区块链应用研究院联合申报的3项发明专利证书。 分别为: 一种基于双账本的物联网数据存储与共享方法 一种面向物联网…...
CleanMyMac X2024破解版mac电脑清理工具
今天,我要跟大家分享一个让我彻底告别电脑卡顿的秘密武器——CleanMyMac X。这不仅仅是一款普通的清理工具,它是你电脑的私人健身教练,让电脑焕发青春活力! CleanMyMac直装官方版下载地址: http://wm.makeding.com/i…...
微软数据库的SQL注入漏洞解析——Microsoft Access、SQLServer与SQL注入防御
说明:本文仅是用于学习分析自己搭建的SQL漏洞内容和原理,请勿用在非法途径上,违者后果自负,与笔者无关;本文开始前请认真详细学习《中华人民共和国网络安全法》及其相关法规内容【学法时习之丨网络安全在身边一图了解网络安全法_中央网络安全和信息化委员会办公室】 。…...
无人机之处理器篇
无人机的处理器是无人机系统的核心部件之一,它负责控制无人机的飞行、数据处理、任务执行等多个关键功能。以下是对无人机处理器的详细解析: 一、处理器类型 无人机中使用的处理器主要包括以下几种类型: CPU处理器:CPU是无人机的…...
828华为云征文 | 华为云Flexus X实例上实现Docker容器的实时监控与可视化分析
前言 华为云Flexus X,以顶尖算力与智能调度,引领Docker容器管理新风尚。828企业上云节之际,Flexus X携手前沿技术,实现容器运行的实时监控与数据可视化,让管理变得直观高效。无论是性能瓶颈的精准定位,还是…...
缓存预热/雪崩/穿透/击穿
1. 缓存预热 预先将MySQL中的数据同步至Redis的过程 2. 缓存雪崩 Redis主机出现故障,或有大量的key同时过期大面积失效导致Redis不可用 Redis中key设置为永不过期,或者过期时间错开Redis缓存集群实现高可用多缓存结合预防雪崩服务降级 3. 缓存穿透 …...
C/C++:优选算法
一、双指针 1.1移动零 链接:283. 移动零 - 力扣(LeetCode) 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操…...
用于大数据分析的数据存储格式:Parquet、Avro 和 ORC 的性能和成本影响
高效的数据处理对于依赖大数据分析做出明智决策的企业和组织至关重要。显著影响数据处理性能的一个关键因素是数据的存储格式。本文探讨了不同存储格式(特别是 Parquet、Avro 和 ORC)对 Google Cloud Platform (GCP) 上大数据环境…...
运维面试别再背八股文了!这15道高频笔试题,我用真实排错案例给你讲透
运维面试突围指南:用真实故障案例拆解15道高频技术题 去年冬天的一个凌晨,我接到了一通紧急电话——某电商平台的支付系统突然瘫痪,每分钟损失超过六位数。当我顶着寒风赶到机房时,发现这只是因为一个简单的NTP时间不同步问题。这…...
JS逆向新手也能搞定:手把手教你用Node.js补全ali140滑块canvas环境(附完整代码)
JS逆向新手也能搞定:手把手教你用Node.js补全ali140滑块canvas环境(附完整代码) 第一次接触JS逆向时,看到那些复杂的加密逻辑和环境检测代码,确实让人望而生畏。特别是遇到canvas这种需要模拟浏览器环境的场景…...
Granite TimeSeries FlowState R1 模型效果深度评测:与传统统计方法的对比
Granite TimeSeries FlowState R1 模型效果深度评测:与传统统计方法的对比 时间序列预测这事儿,听起来挺专业,其实离我们生活很近。比如,电商平台要预测下个月的销售额,电力公司要预估明天的用电负荷,甚至…...
逆向分析实战:从IDA反编译看bjdctf_2020_babystack的栈溢出漏洞成因与利用
逆向工程实战:bjdctf_2020_babystack栈溢出漏洞的深度解析 在二进制安全领域,栈溢出漏洞始终是攻防对抗的经典课题。今天我们将以bjdctf_2020_babystack这道CTF题目为案例,通过IDA Pro的静态分析视角,完整还原从漏洞发现到利用的…...
如何快速掌握PathOfBuilding:流放之路终极角色构建规划神器
如何快速掌握PathOfBuilding:流放之路终极角色构建规划神器 【免费下载链接】PathOfBuilding Offline build planner for Path of Exile. 项目地址: https://gitcode.com/GitHub_Trending/pa/PathOfBuilding 还在为《流放之路》复杂的天赋树和装备搭配而头疼…...
WarcraftHelper:魔兽争霸3现代系统兼容性优化终极指南 [特殊字符]
WarcraftHelper:魔兽争霸3现代系统兼容性优化终极指南 🎮 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为魔兽争霸3在现…...
避坑指南:解决多Livox雷达在ROS中TF树报错‘extrapolation into the past’的完整流程
避坑指南:解决多Livox雷达在ROS中TF树报错‘extrapolation into the past’的完整流程 当你在ROS系统中整合多个Livox雷达时,突然遭遇[WARN] Lookup would require extrapolation into the past的警告信息,Rviz中点云显示异常或TF树断裂&…...
清华团队ISSCC新作解读:用eDRAM-LUT做存内计算,如何把存储单元变成加法器?
清华团队ISSCC新作:eDRAM-LUT如何重构存内计算芯片设计范式 当人工智能模型的参数量突破千亿级别时,传统计算架构的"存储墙"问题愈发凸显。数据在存储器和处理器之间的频繁搬运,消耗了系统60%以上的能耗——这恰恰是存内计算技术试…...
CosyVoice集成Java Web应用:构建智能语音播报后端服务
CosyVoice集成Java Web应用:构建智能语音播报后端服务 最近在做一个在线教育平台的项目,需要给课程内容加上语音播报功能。一开始我们试过一些现成的语音合成服务,要么价格太贵,要么声音不够自然。后来发现星图GPU平台上有个Cosy…...
告别Armoury Crate臃肿困扰:GHelper革新性华硕笔记本硬件控制方案带来性能突破
告别Armoury Crate臃肿困扰:GHelper革新性华硕笔记本硬件控制方案带来性能突破 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops. Control tool for ROG Zephyrus G14, G15, G16, M16, Flow X13, Flow X16, TUF, Strix, Scar and…...
