C++的模板(九):模板的实例化问题
前文子系统中的例子, SubSystem内部用了STL库的map模板:
template <class Event, class Response>
class SubSystem{
public:map<Event*, Response*> table;
public:void bind(Event *e, Response *r);void unbind(Event *e);
public:int OnMessage(Event *e);
};
而作为Key来使用的Event类型,就事论事而言,到这里只是一个整数数据的简单包装:
class Event {
public:int ev_id;~Event(){printf("~Event(id_%d)\n", ev_id);}
};
那么直接用Event类而不是用Event指针来构造map是不是更有效?确实。在不考虑将来有Event派生类的情况下,在这个例子可以这样改进。这样:
template <class Event, class Response>
class SubSystem{
public:map<Event, Response*> table;
public:void bind(Event *e, Response *r);void unbind(Event *e);
public:int OnMessage(Event *e);
};
同时,成员函数中指针的使用也要相应调整。这样SubSystem类就改完了。编译… 。预计直接通过,但编译报了很多错,刷了几屏都翻不过来… 。用过STL库工程师们都有过这种经验吧!
什么原因呢,STL中的容器,对用作key的类型是有些讲究的,key必须能够比较,而这里的Event类没写“operator<”运算。这就导致模板对key引用发生问题,模板内的一些函数或变量没法生成,发生雪崩效应,进而导致更多的引用错误报出来。编程中遇到这种情况,不用紧张,报的都是虚假的错误,只要找到模板的参数类,看看哪里没写完整,轻轻一改所有错误就会立即消失。
这里检查看到是Event类少了“operator<”运算符重载引起的问题。加上它就行了。几百个错误,两三句话就改完了:
class Event {
public:int ev_id;~Event(){printf("~Event(id_%d)\n", ev_id);}bool operator<(const Event &e) const { return ev_id<e.ev_id; }
};
当然这不是唯一的办法。如果不想改Event ,改less也可以。错误都是因为缺少“operator<”运算符,模板中的less实例化失败引起的。接着产生了连锁反应。直接把针对 Event的特殊的less加上就解决了问题。用less特化。因为less是std名称空间定义的模板,特化需要在同一名称空间进行:
class Event {
public:int ev_id;~Event(){printf("~Event(id_%d)\n", ev_id);}
};
namespace std{
template <>
struct less<Event> {bool operator()(const Event &e, const Event &e1) const {return e.ev_id<e1.ev_id;}
};
}
这样Event就不用改。或者,不从std名称空间改,重开一个less模板,如果参数类型是Event就特化,否则,就继承 std::less模板(是的,继承就不用写代码了),等等,都行:
namespace dts {
template <typename T>
struct less : std::less<T> {
};
template <>
struct less<Event> {bool operator()(const Event &e, const Event &e1) const {return e.ev_id<e1.ev_id;}
};
}template <class Event, class Response>
class SubSystem{
public:map<Event, Response*,dts::less<Event> > table;
public:void bind(Event *e, Response *r);void unbind(Event *e);
public:int OnMessage(Event *e);
};
map模板是可以重新设置less参数的。这样,问题就解决了。
另外,还有个更狠的办法,可以叫编译器立即闭嘴。向STL容器传入自定义类型的指针,而不是自定义类型本身。因为指针直接带有容器需要的所有运算符,这样编译器就再也不会报错了。
但这样容器的find函数也就不能再用了。恰好子系统的例子中就有一个这样的find,现在就来看看find:
Event *find(list<Event*> &l, Event &e)
{list<Event*>::iterator i;for(i=l.begin(); i!=l.end(); i++) {if ((*i)->ev_id==e.ev_id) return *i;}return 0;
}
list中存的是Event的指针,STL库的find需要相同的类型,也就是用Event的指针去找,如果找到,就给你一个你本来就已经有了的Event指针。看起来这像个悖论。但库的逻辑就是这样。所以子系统的例子就自己写了一个find。
如果不想自己写,还可以用STL库的find_if模板。它有个pred参数。这是重载了()运算符的仿函数。仿函数唯一的功能就是重载了()运算符。除此以外就是初始化。下面的Match就是仿函数,用来匹配find_if模板的pred参数:
struct Match {Event ev;bool operator()(Event* e) {return ev.ev_id==e->ev_id;}
};
Event *find(list<Event*> &l, Event &e)
{list<Event*>::iterator i;Match m;m.ev=e;i = std::find_if(l.begin(), l.end(), m);if(i!=l.end())return *i;return 0;
}
find_if的比较就很灵活了。但现在Match出现在全局名称空间。如果不想这个小不点污染名称空间,可以把它挪到任何一个关联的类里面去。而class Event看起来最合适。这就把它挪过去:
class Event {
public:int ev_id;~Event(){printf("~Event(id_%d)\n", ev_id);}struct Match {Event *ev;bool operator()(Event* e) {return ev->ev_id==e->ev_id;}};
};
看起来最合适,却需要改一改。因为放在这里 Event成了未定义完成的类, Match中不能直接用,所以改成用它的指针。相关代码也调整一下。这样就好了。
当然也可以向pred传入普通的比较函数,但find_if只向它传一个参数,另一个参数要自己想办法了:
Event *find(list<Event*> &l, Event &e)
{iterator i;static int ev_id;struct Match{static bool p(Event *e) {return ev_id==e->ev_id;}};ev_id=e.ev_id;i = std::find_if(l.begin(), l.end(), Match::p);if( i!=l.end()) return *i;return 0;
}
如果也不想用这种方法,还有没有别的办法?答案是,有的。最后还是可以回到STL的标准的find上来。直接用当然是不行,但是可以重载iterator迭代器:
class iterator: public list<Event*>::iterator{
public:Event& operator*() {return *(list<Event*>::iterator::operator*());}iterator &operator=(list<Event*>::iterator i) {list<Event*>::iterator::operator=(i);return *this;}
} ;Event *find(list<Event*> &l, Event &e)
{::iterator i, begin, end;begin= l.begin();end= l.end();i= std::find(begin, end, e);if(i!=end) return &*i;return 0;
}
iterator 这个名字跟std预定义的名字冲突了。所以用的时候带上了作用域分辨符,否则就不是这里的class iterator了。因为重载了*,return &*i; 意思就不是return i; 了。最后编译后报告Event类型少了个==运算,把它补上。这样也通过了。
相关文章:
C++的模板(九):模板的实例化问题
前文子系统中的例子, SubSystem内部用了STL库的map模板: template <class Event, class Response> class SubSystem{ public:map<Event*, Response*> table; public:void bind(Event *e, Response *r);void unbind(Event *e); public:int OnMessage(E…...
Clickhouse Projection
背景 Clickhouse一个视图本质还是表,只支持一种order By,不然要维护太多的视图。 物化视图能力有限。 在设计聚合功能时,考虑使用AggregatingMergeTree表引擎,现在有了projections,打算尝试使用一下 操作 ADD PROJE…...

放烟花短视频素材去哪里找?去哪里下载?烟花素材网分享
在当代社会,短视频凭借其独有的魅力成为大众传递情感、记录生活、分享快乐的新兴方式。特别是在庆祝节日和特殊时刻时,烟花的绚丽效果常常被用来吸引观众的目光,成为视频作品中的亮点。然而,对于短视频制作者来说,寻找…...

爬虫笔记14——爬取网页数据写入MongoDB数据库,以爱奇艺为例
下载MongoDB数据库 首先,需要下载MongoDB数据库,下载的话比较简单,直接去官网找到想要的版本下载即可,具体安装过程可以看这里。 pycharm下载pymongo库 pip install pymongo然后在在python程序中我们可以这样连接MongoDB数据库…...

Jenkins教程-10-发送飞书测试报告通知
上一小节我们学习了发送企业微信测试报告通知的方法,本小节我们讲解一下发送飞书测试报告通知的方法。 1、自动化用例执行完后,使用pytest_terminal_summary钩子函数收集测试结果,存入本地status.txt文件中,供Jenkins调用 conft…...

Swift开发——简单App设计
App的界面设计需要具有大量的图像并花费大量的时间,这样的应用不方便学习和交流,这里重点介绍SwiftUI界面元素的用法,通过简单App设计过程的讲解,展示图形用户界面应用程序的设计方法。 01、简单App设计 按照9.1节工程MyCh0901的创建方法,创建一个新的工程MyCh0902,此时工…...

Python操作mysql
一、python连接mysql 1.python连接mysql代码示例 from pymysql import Connection# 获取到mysql数据艰苦的连接对象 conn Connection(hostlocalhost,port3306,userroot,passwordroot ) # 打印mysql数据库软件信息 print(conn.get_server_info()) # 关闭到数据库的连接 conn.…...
监控易产品升级动态:V7.6.6.15版本全面升级
随着信息技术的不断发展,企业对系统监控和数据管理的需求日益增加。为了满足广大用户的实际需求,监控易团队经过不懈努力,成功推出了V7.6.6.15版本,对产品进行了全面升级和优化。本次升级不仅增强了产品的稳定性和可靠性ÿ…...

Vue3 + Element-plus + TS —— 动态表格自由编辑
前期回顾 《 穿越时空的代码、在回首:Evil.js两年后的全新解读 》-CSDN博客 Vue3 TS Element-Plus 封装Tree组件 《亲测可用》_ https://blog.csdn.net/m0_57904695/article/details/131664157?spm1001.2014.3001.5501 态表格 自由编辑 目录 ♻️ 效果图…...

虚拟机配置桥接模式
背景 因为要打一些awd比赛,一些扫描工具什么的,要用到kali,就想着换成一个桥接模式 但是我看网上的一些文章任然没弄好,遇到了一些问题 前置小问题 每次点开虚拟网络编辑器的时候都没有vmnet0,但是点击更改的时候却有vmnet0 第一步: 点击更改设置 第二步: 把wmnet0删掉 …...
星戈瑞DSPE-SS-PEG-CY7近红外花菁染料
DSPE-SS-PEG-CY7是一种具有复杂而精细结构的复合纳米材料,其在生物医学领域的应用增多。该材料结合了磷脂(DSPE)、聚乙二醇(PEG)、二硫键(SS)以及荧光染料(CY7)的特点&am…...
LeetCode:503. 下一个更大元素 II(Java 单调栈)
目录 503. 下一个更大元素 II 题目描述: 实现代码与解析: 单调栈 原理思路: 503. 下一个更大元素 II 题目描述: 给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] )&…...
代码重构:解读重构概念及重构实战
目录 一.重构是什么(what) 1.重构的本质 2.重构≠性能优化 二.重构的目的(why) 1.去写好的代码 2.去写更灵活的代码 三.重构的时机(when and where) 1.何时重构 2.何时不重构 四.重构的方法(how) 1.重构关键核心 2.重构方法 3.重构工具 小结 一.重构是什么(what)…...
java.util.Optional类介绍
java.util.Optional 是 Java 8 引入的一个容器类,用于表示可能包含或不包含非空值的对象。它的设计初衷是为了减少程序中的空指针异常(NullPointerException),并使代码更加简洁和易读。 Optional 类的介绍 1. 特点 避免显式的 null 检查:使用 Optional 可以避免显式的 n…...

PhotoShop自动生成号码牌文件
1、说明 设计卡牌的时候,遇到自动生成编号,从01500到-02500,一个一个的手写,在存储保存成psd格式的文件,会很耗时。 下面将介绍如何使用ps自动生成psd格式的文件 2、使用excle生成数字 从01500到-02500 第一步&…...

02逻辑代数与硬件描述语言基础
2.1 逻辑代数(简单逻辑的运算) 2.2 逻辑函数的卡诺图(从图论的角度)化简法 2.3 硬件描述语言Verilog HDL基础(研究生阶段才用得到) 要求: 1、熟悉逻辑代数常用基本定律、恒等式和规则。 2、掌握…...

OpenGL3.3_C++_Windows(21)
抗锯齿 遇到模型边缘有锯齿:光栅器将顶点数据转化为片段的方式有关 抗锯齿:产生更平滑的边缘SSAA超采样抗锯齿:使用比正常分辨率更高的分辨率,来渲染场景,它也会带来很大的性能开销。 光栅器: 位于最终处…...

clickhouse学习
ClickHouse学习 安装部署 1.下载rpm文件 下载地址:https://packages.clickhouse.com/rpm/stable/ clickhouse-client-23.2.1.2537.x86_64.rpm clickhouse-common-static-23.2.1.2537.x86_64.rpm clickhouse-common-static-dbg-23.2.1.2537.x86_64.rpm clickhous…...

MySQL高级-索引-使用规则-前缀索引
文章目录 1、前缀索引2、前缀长度3、查询表数据4、查询表的记录总数5、计算并返回具有电子邮件地址(email)的用户的数量6、从tb_user表中计算并返回具有不同电子邮件地址的用户的数量7、计算唯一电子邮件地址(email)的比例相对于表…...
外星生命在地球的潜在存在:科学、哲学与社会的交织
外星生命在地球的潜在存在:科学、哲学与社会的交织 摘要:近年来,关于外星生命是否存在的讨论日益激烈。有研究表明,外星人可能已经在地球漫步,这一观点引发了广泛的科学、哲学和社会学思考。本文将从科学角度探讨外星…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...

WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...

《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...

从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...

OPENCV图形计算面积、弧长API讲解(1)
一.OPENCV图形面积、弧长计算的API介绍 之前我们已经把图形轮廓的检测、画框等功能讲解了一遍。那今天我们主要结合轮廓检测的API去计算图形的面积,这些面积可以是矩形、圆形等等。图形面积计算和弧长计算常用于车辆识别、桥梁识别等重要功能,常用的API…...
RLHF vs RLVR:对齐学习中的两种强化方式详解
在语言模型对齐(alignment)中,强化学习(RL)是一种重要的策略。而其中两种典型形式——RLHF(Reinforcement Learning with Human Feedback) 与 RLVR(Reinforcement Learning with Ver…...