一个C++模板工厂的编译问题的解决。针对第三方库的构造函数以及追加了的对象构造函数。牵扯到重载、特化等
一窥模板的替换和匹配方式:偏特化的参数比泛化版本的还要多:判断是不是std::pair<,>。_stdpair模板参数太多-CSDN博客
简介
在一个项目里,调用了第三封的库,这个库里面有个类用的很多,而且其构造函数至少有6个,并且个人感觉还不够多。根据实际使用,还得增加一些。
需求
1、增加构造函数,比如除了下面的,还增加(ElementId,const std::wstring& modelName),以及FarElementId
2、希望能用通用的构造接口来生成对象,比如Create
3、希望能用指针对象(智能指针)
第三方库的6个构造函数
EditElementHandle (MSElementDescrCP descr, bool isUnmodified, DgnModelRefR modelRef);
EditElementHandle() {}
EditElementHandle (MSElementDescrP descr, bool owned, bool isUnmodified, DgnModelRefP modelRef=NULL) ;
EditElementHandle (ElementRefP elRef, DgnModelRefP modelRef=NULL) : ElementHandle (elRef, modelRef) {}
EditElementHandle (MSElementCP el, DgnModelRefP modelRef) : ElementHandle (el, modelRef){}
EditElementHandle (ElementId id, DgnModelRefP modelRef) : ElementHandle (id, modelRef) {}
定义智能指针
using EditElementHandlePtr = std::shared_ptr<DgnPlatform::EditElementHandle>;
定义工厂EEHFactory
struct EEhFactory
{template <typename ... Args>static HCHXKERNEL::EditElementHandlePtrCreate(Args&& ... args){return std::make_shared<DgnPlatform::EditElementHandle>(std::forward<Args>(args)...);}
};
可以这样使用:
ElementRefP elRef{ NULL };
DgnModelRefP modelRef{ NULL };
auto editElementHandlePtr1 = EEhFactory::Create(elRef, modelRef);
新需求
我想这样使用,怎么办:
DgnPlatform::ElementId eid0{ 0 };
std::wstring str{ L"" };
auto editElementHandlePtr2 = EEhFactory::Create(eid0, str);或者
auto editElementHandlePtr3 = EEhFactory::Create(eid0, std::wstring{ L"PipeDrawing" });或者
auto editElementHandlePtr3 = EEhFactory::Create(eid0, L"PipeDrawing" );
问题:相当于增加了两个构造函数。这两个函数由我们自己实现
第一个尝试:非泛化版本
先加入普通静态函数,看什么效果,能否达到重载的目的:
struct EEhFactory
{template <typename ... Args>static HCHXKERNEL::EditElementHandlePtrCreate(Args&& ... args){return std::make_shared<DgnPlatform::EditElementHandle>(std::forward<Args>(args)...);}static HCHXKERNEL::EditElementHandlePtrCreate(ElementId eid, const std::wstring& modelName)
{DgnModelRefP modelRef = NULL;if (!MiscUtil::GetModelRefByName(&modelRef, modelName.c_str(), false, false))return NULL;return EEhFactoryXXX::Create(eid, modelRef);
}
};调用:
{DgnPlatform::ElementId eid0{ 0 };//, eid1{ 0 };DgnModelRefP modelRef = NULL;EEhFactoryXXX::Create(eid0, modelRef);
}
编译结果是:
注意最后一句:
note: 参见对正在编译的函数 模板 实例化“std::shared_ptr<
Bentley::DgnPlatform::EditElementHandle
>
EEhFactoryXXX::Create<Bentley::DgnPlatform::ElementId&,std::wstring&>
(Bentley::DgnPlatform::ElementId &,std::wstring &)”的引用
明明我们想要调用的是:
static HCHXKERNEL::EditElementHandlePtr Create(ElementId eid, const std::wstring& modelName);
结果看起来,貌似编译器优先去泛型那边了:
template <typename ... Args>static std::shared_ptr<DgnPlatform::EditElementHandle>/*HCHXKERNEL::EditElementHandlePtr*/Create(Args&& ... args){return std::make_shared<DgnPlatform::EditElementHandle>(std::forward<Args>(args)...);}
这个第26行,就是泛型函数里的,压根没走到我们的普通函数那里。
第二个尝试:泛化版本
改造一下,想法是推导的时候,
Param1st推导成ElementId,而Param2ndst推导成std::wstring:
struct EEhFactoryXXX
{template <typename ... Args>static std::shared_ptr<DgnPlatform::EditElementHandle>/*HCHXKERNEL::EditElementHandlePtr*/Create(Args&& ... args){return std::make_shared<DgnPlatform::EditElementHandle>(std::forward<Args>(args)...);}template<typename T>struct IsFarElementID : std::false_type{};template<>struct IsFarElementID<DgnPlatform::DgnHistory::FarElementID> : std::true_type{};template<typename T>struct IsElementId : std::false_type{};template<>struct IsElementId<DgnPlatform::ElementId> : std::true_type{};template<typename T>struct IsWstring : std::false_type{};template<>struct IsWstring<std::wstring> : std::true_type{};template <typename Param1st, typename Param2ndst,typename std::enable_if<IsElementId<Param1st>::value &&IsWstring<Param2ndst>::value, int>::type = 0>static HCHXKERNEL::EditElementHandlePtr Create(Param1st eid,const Param2ndst& modelName){DgnModelRefP modelRef = NULL;if (!MiscUtil::GetModelRefByName(&modelRef, modelName.c_str(), false, false))return NULL;//using eid_decayType = typename std::decay<Param1st>::type;//typename std::decay<Param1st>::type a;//TypeDisplayer<decltype(a)> aType;//return NULL;return EEhFactoryXXX::Create(eid, modelRef);}
};调用:
{DgnPlatform::ElementId eid0{ 0 };//, eid1{ 0 };std::wstring str{ L"" };auto editElementHandlePtr2 = EEhFactoryXXX::Create(eid0, str);
}
但是呢,编译结果让人大吃一惊,出现的编译提示和之前一模一样,说明压根还是没走到我们新的泛型函数里来。
这次终于引起我的注意了
Param1st推导成Bentley::DgnPlatform::ElementId&?而Param2ndst推导成std::wstring &?
至于我期望的Elementid,和const std::wstring&,压根不挨着?const跑哪去了,压根没搭理我给出的定义?
而且还是走的原来的变参的模板定义。
Bentley::DgnPlatform::EditElementHandle
>
EEhFactoryXXX::Create<Bentley::DgnPlatform::ElementId&,std::wstring&>
(Bentley::DgnPlatform::ElementId &,std::wstring &)”的引用
果然泛型这玩意,水太深了。
分析函数重载
睡觉之前琢磨了一下,感觉有头绪了
首先,类EEHFactory里的两个Create函数,都是static的泛型成员变量。一个是最泛化的版本,另一个是特化的版本。
这貌似牵扯到函数重载的问题?
但看看这个:函数模板之间不能重载,把类型推导放到形参里就可以了_template 函数无法重载-CSDN博客
是不是要分两种情况,是不是存在最泛化的版本。
存在最泛化的版本时
而且当前这个泛化的版本,是属于变参的,什么类型都能接受的那种。
编译器扫描到存在一个这样的泛化版本,先用它解析了一下参数类型。由于它是引用类型(Args&& ... args),左值的变量就推导出了左值引用,
{DgnPlatform::ElementId eid0{ 0 };std::wstring str{ L"" };auto editElementHandlePtr2 = EEhFactoryXXX::Create(eid0, str);
eid0和str都是左值变量,最后推到出来的类型就是左值引用:ElementId&和std::wstring&
}
auto editElementHandlePtr2 =
EEhFactoryXXX::Create(eid0, std::wstring{L"PipeDrawing"});
的推导结果就是ElementId&,和std::wstring&&
好了,这个泛化版本先把类型给推导出来了,基调给定下来了,那就是ElementId&和std::wstring&。这个时候,它发现另一个Create函数,貌似可以重载,但得先检查一下,参数是否匹配。
它发现,此Create的参数是:Param1st eid和const Param2ndst& modelName
从形式上看,就不符合。
所以,最后还是走最泛化的版本。
template <typename Param1st, typename Param2ndst,typename std::enable_if<IsElementId<Param1st>::value &&IsWstring<Param2ndst>::value, int>::type = 0>static HCHXKERNEL::EditElementHandlePtr Create(Param1st eid,const Param2ndst& modelName){DgnModelRefP modelRef = NULL;if (!MiscUtil::GetModelRefByName(&modelRef, modelName.c_str(), false, false))return NULL;return EEhFactoryXXX::Create(eid, modelRef);}
那我就让你如意,重新定义一下:
把Param1st eid和const Param2ndst& modelName改成:
Param1st& eid和Param2ndst& modelName。
就没事了。
struct EEhFactoryXXX
{template <typename ... Args>static std::shared_ptr<DgnPlatform::EditElementHandle>/*HCHXKERNEL::EditElementHandlePtr*/Create(Args&& ... args){return std::make_shared<DgnPlatform::EditElementHandle>(std::forward<Args>(args)...);}template<typename T>struct IsFarElementID : std::false_type{};template<>struct IsFarElementID<DgnPlatform::DgnHistory::FarElementID> : std::true_type{};template<typename T>struct IsElementId : std::false_type{};template<>struct IsElementId<DgnPlatform::ElementId> : std::true_type{};template<typename T>struct IsWstring : std::false_type{};template<>struct IsWstring<std::wstring> : std::true_type{};template <typename Param1st, typename Param2ndst,typename std::enable_if<IsElementId<Param1st>::value &&IsWstring<Param2ndst>::value, int>::type = 0>static HCHXKERNEL::EditElementHandlePtr Create(Param1st& eid,Param2ndst& modelName){DgnModelRefP modelRef = NULL;if (!MiscUtil::GetModelRefByName(&modelRef, modelName.c_str(), false, false))return NULL;return EEhFactoryXXX::Create(eid, modelRef);}
};调用:
{DgnPlatform::ElementId eid0{ 0 };//, eid1{ 0 };std::wstring str{ L"" };auto editElementHandlePtr2 = EEhFactoryXXX::Create(eid0, str);
}
下面这个例子,帮助理解,但这是个类的例子,函数可能有所不同,但仅仅是帮助理解。
A的泛化版本根本没有实现,但它是不能缺少的,否则就编译报错。
第二个A是个偏特化版本。(还是特化?)
enable_if和类的偏特化-CSDN博客
#include <iostream>
template<class T, class Enabled=void >
class A;template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
public:A() { std::cout << "partial specialization\r\n";}
}; // specialization for floating point typesint main() {A<double> a;
}
不存在最泛化版本时
编译器别无选择。可以编译成功。
但如果是类,必须要有最泛化的版本。
struct EEhFactoryXXX
{///@code{.unparsed}///此函数的功能:/// 创建EditElementHandle的指针对象/// 最泛化的版本///@endcode ///@return true:成功 false:失败///@author Simon.Zou @date 2024/02/20//template <typename ... Args>//static std::shared_ptr<DgnPlatform::EditElementHandle>/*HCHXKERNEL::EditElementHandlePtr*/// Create(Args&& ... args)//{// return std::make_shared<DgnPlatform::EditElementHandle>(std::forward<Args>(args)...);//}template<typename T>struct IsFarElementID : std::false_type{};template<>struct IsFarElementID<DgnPlatform::DgnHistory::FarElementID> : std::true_type{};template<typename T>struct IsElementId : std::false_type{};template<>struct IsElementId<DgnPlatform::ElementId> : std::true_type{};template<typename T>struct IsWstring : std::false_type{};template<>struct IsWstring<std::wstring> : std::true_type{};template <typename Param1st, typename Param2ndst,typename std::enable_if<IsElementId<Param1st>::value &&IsWstring<Param2ndst>::value, int>::type = 0>static HCHXKERNEL::EditElementHandlePtr Create(Param1st& eid,Param2ndst& modelName){DgnModelRefP modelRef = NULL;if (!MiscUtil::GetModelRefByName(&modelRef, modelName.c_str(), false, false))return NULL;return NULL; //return EEhFactoryXXX::Create(eid, modelRef);
//暂时切换成return NULL。因为没有函数接收ElementId和DgnModelRefp}
//
// template <
// typename Param1st, typename Param2ndst,
// typename std::enable_if<
// IsElementId<Param1st>::value &&
// IsWstring<Param2ndst>::value
// , int>::type = 0
// >
// static HCHXKERNEL::EditElementHandlePtr Create(
// Param1st& eid,
// Param2ndst&& modelName
// )
// {
// return EEhFactoryXXX::Create(eid, modelName);
// }};测试:
DgnPlatform::ElementId eid0{ 0 };//, eid1{ 0 };
std::wstring str{ L"" };
auto editElementHandlePtr2 = EEhFactoryXXX::Create(eid0, str);
相关文章:

一个C++模板工厂的编译问题的解决。针对第三方库的构造函数以及追加了的对象构造函数。牵扯到重载、特化等
一窥模板的替换和匹配方式:偏特化的参数比泛化版本的还要多:判断是不是std::pair<,>。_stdpair模板参数太多-CSDN博客 简介 在一个项目里,调用了第三封的库,这个库里面有个类用的很多,而且其构…...
《昇思 25 天学习打卡营第 20 天 | Pix2Pix实现图像转换 》
《昇思 25 天学习打卡营第 20 天 | Pix2Pix实现图像转换 》 活动地址:https://xihe.mindspore.cn/events/mindspore-training-camp 签名:Sam9029 Pix2Pix模型概述 Pix2Pix是一种基于条件生成对抗网络(cGAN)的图像转换模型&#x…...

关于c#的简单应用三题
#region 输入一个正整数,求1~这个数的阶乘 public static void Factorial(int a) { int result 1; for (int i 1; i < a; i) { result result * i; } Console.WriteLine(result); } #endregion #region 一个游戏&#…...

(十三)Spring教程——依赖注入之工厂方法注入
1.工厂方法注入 工厂方法是在应用中被经常使用的设计模式,它也是控制反转和单例设计思想的主要实现方法。由于Spring IoC容器以框架的方式提供工厂方法的功能,并以透明的方式开放给开发者,所以很少需要手工编写基于工厂方法的类。正是因为工厂…...
Redission中的Lua脚本写法、理解
对于Redission看门狗机制中的为了保证原子性的Lua脚本的写法规则是什么样的呢 ? 对于源码中的Lua脚本又是什么意思? 我们一起来看一下 首先,我们先基本的熟悉一下lua脚本的逻辑 在Lua脚本中,if (…) then … end 语句的执行过程…...

视频共享融合赋能平台LntonCVS视频监控管理平台视频云解决方案
LntonCVS是基于国家标准GB28181协议开发的视频监控与云服务平台,支持多设备同时接入。该平台能够处理和分发多种视频流格式,包括RTSP、RTMP、FLV、HLS和WebRTC。主要功能包括视频直播监控、云端录像与存储、检索回放、智能告警、语音对讲和平台级联&…...

GraphRAG + GPT-4o mini 低成本构建 AI 图谱知识库
更好的效果,更低的价格,听起来是不是像梦呓? 限制 首先,让我们来介绍一个词:RAG。 简单来说,RAG(Retrieval-Augmented Generation,检索增强生成) 的工作原理是将大型文档…...
全国区块链职业技能大赛第十套区块链产品需求分析与方案设计
任务1-1:区块链产品需求分析与方案设计 养老保险平台中涉及到参保人、社保局、公安局、工作单位等参与方,他们需要在区块链养老保险平台中完成账户注册、身份上链、社保代缴、信息核查等多种业务活动。通过对业务活动的功能分析,可以更好的服务系统的开发流程。基于养老保险…...

分布式Apollo配置中心搭建实战
文章目录 环境要求第一步、软件下载第二步、创建数据库参考文档 最近新项目启动,采用Apollo作为分布式的配置中心,在本地搭建huanj 实现原理图如下所示。 环境要求 Java版本要求:JDK1.8 MySql版本要求:5.6.5 Apollo版本要求&…...
Android monkey命令和monkey脚本详解
Monkey命令 monkey 是 Android 平台上一个非常有用的工具,它可以帮助开发者在设备上生成随机的用户事件流,如按键输入、触摸屏手势等,以此来测试应用的稳定性。这对于发现应用中的崩溃、异常和性能问题特别有用。 基本语法 adb shell monk…...
vue 实现对图片的某个区域点选, 并在该区域上方显示该部分内容
目录 1、通义灵码实现: 2、csdn的C知道: 3、百度comate: 1、通义灵码实现: 在 Vue 中实现对图片某个区域的点选并显示该区域属于哪一部分,通常涉及到几个关键步骤: 图片区域划分: 首先&#…...
配置文件格式 INI 快速上手
文章目录 1.简介2.语法节键值对注释大小写空白行数据类型字符串 (String)整数 (Integer)浮点数 (Float)布尔值 (Boolean)列表 (List) 3.示例4.解析参考文献 1.简介 INI 的全称是 Initialization,即为初始化文件,最早是 Windows 系统配置文件所采用的格式…...

基于WebGoat平台的SQL注入攻击
目录 引言 一、安装好JAVA 二、下载并运行WebGoat 三、注册并登录WebGoat 四、模拟攻击 1. 第九题 2. 第十题 3. 第十一题 4. 第十二题 5. 第十三题 五、思考体会 1. 举例说明SQL 注入攻击发生的原因。 2. 从信息的CIA 三要素(机密性、完整性、可用性&…...

SpringMvc有几个上下文
你好,我是柳岸花明。 SpringMVC作为Spring框架的重要组成部分,其启动流程和父子容器机制是理解整个框架运行机制的关键。本文将通过一系列详细的流程图,深入剖析SpringMVC的启动原理与父子容器的源码结构。 SpringMVC 父子容器 父容器的创建 …...

k8s部署rabbitmq集群
1 部署集群 1.1 安装 # 创建一个中间件的命名空间 kubectl create namespace middleware # 创建ConfigMap,包含RabbitMQ的配置文件内容 kubectl apply -f rabbitmq-configmap.yaml # 配置用于存储RabbitMQ数据的PersistentVolume(PV)和PersistentVolum…...
Python利用包pypinyin汉字转拼音(处理多音字)
一、汉字转拼音 在python中将汉字的拼音输出可以采用pypinyin包,一下是简单的demo示例: 默认调用pinyin方法转换时时默认时带声调的,不带声调需要添加“styleStyle.NORMAL”参数。 from pypinyin import pinyin, Styledef pinyin_transfer…...

推荐系统三十六式学习笔记:工程篇.常见架构24|典型的信息流架构是什么样的
目录 整体框架数据模型1.内容即Activity2.关系即连接 动态发布信息流排序数据管道总结 从今天起,我们不再单独介绍推荐算法的原理,而是开始进入一个新的模块-工程篇。 在工程实践的部分中,我首先介绍的内容是当今最热门的信息流架构。 信息…...
解决QEMU无法从非0x80000000处开始执行
解决QEMU无法从非0x80000000处开始执行 1 背景介绍2 问题描述3 原因分析4 解决办法5 踩坑回忆5.1 坑1 - 怀疑设备树有问题5.2 坑2 - 怀疑QEMU中内存未写入成功5.3 QEMU地址空间分析过程 1 背景介绍 在使用NEMU与QEMU做DiffTest的场景下,运行的固件为《RISC-V体系结…...

AI在候选人评估中的作用:精准筛选与HR决策的助力
一、引言 随着科技的迅猛发展,人工智能(AI)技术已逐渐渗透到各个行业和领域,人力资源管理(HRM)亦不例外。在候选人评估的环节中,AI技术以其高效、精准的特性,正在逐步改变着传统的招…...
自动化测试的艺术:Xcode中GUI测试的全面指南
自动化测试的艺术:Xcode中GUI测试的全面指南 在软件开发过程中,图形用户界面(GUI)测试是确保应用质量和用户体验的关键环节。Xcode,作为苹果的官方集成开发环境(IDE),提供了一套强大…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...