c++新经典模板与泛型编程:const修饰符的移除与增加
const修饰符的移除
让你来写移除const修饰符,你会怎么样来写?
😂😂trait
类模板,如下
#include <iostream>// 泛化版本
template<typename T>
struct RemoveConst
{using type = T;
};// 特化版本
template<typename T>
struct RemoveConst<const T>
{using type = T;
};// 根据需要,可能还要增加其他特化版本
template<typename T>
using RemoveConst_t = typename RemoveConst<T>::type;int main()
{// nca 是int类型// c++标准库中的std::remove_const也比较类似RemoveConst_t<const int> nca = 15;// 可以给nca重新赋值nca = 18;return 0;
}
退化技术
- 某些类型一旦传递给函数模板(通过函数模板来推断相关的类型),那么推断出来的类型就会产生退化。所谓退化(decay),就是把类型中的一些修饰符丢弃了。例如,const int中的const丢弃后,就变成int类型,那么对于const int类型,int类型就是一种退化的表现。
c++
标准库中有一个类模板std::decay
,这个类模板的作用就是把一个类型退化掉(就是把类型中的一些修饰符丢掉)。
std::decay<const int&>::type nb = 28;// nb的类型为int类型std::cout << "nb的类型为:" << typeid(decltype(nb)).name() << std::endl;
如何实现一个类似std::decay
功能的trait
类模板呢?
// b.cpp
int g_array[10];// main.cpp
#include <iostream>// 泛化版本
template<typename T>
struct RemoveReference
{using type = T;
};// 特化版本
template<typename T>
struct RemoveReference<T&>
{using type = T;
};
template<typename T>
struct RemoveReference<T&&> // 这个特化能适应 const T&&应该是伴随我一生,难以理解的噩梦了
{using type = T;
};// 泛化版本
template<typename T>
struct RemoveConst
{using type = T;
};// 特化版本
template<typename T>
struct RemoveConst<const T>
{using type = T;
};// 根据需要,可能还要增加其他特化版本
template<typename T>
using RemoveConst_t = typename RemoveConst<T>::type;template<typename T>
struct RemoveCR : RemoveConst<typename RemoveReference<T>::type>
{ // 把const和引用修饰符去掉
};template<typename T>
using RemoveCR_t = typename RemoveCR<T>::type;// Decay的trait类模板
// 泛化版本
template<typename T>
struct Decay : RemoveCR<T>
{
};// 特化版本,这个特化版本没有继承任何父类
// 有边界数组转换成指针
template<typename T,std::size_t size>
struct Decay<T[size]>
{using type = T*;
};// 无边界数组转换成指针
template<typename T>
struct Decay<T[]>
{using type = T*;
};extern int g_array[];int main()
{RemoveCR_t<const int&&> rcrobj = 15; // rcrobj为int类型,只能叹为观止,惊叹rcrobj鬼斧神工地成了int类型int arr[2] = { 1,2 };Decay<decltype(arr)>::type my_array;std::cout << "my_array的类型为: " << typeid(decltype(my_array)).name() << std::endl;Decay<decltype(g_array)>::type my_array_2;std::cout << "my_array_2的类型为:" << typeid(decltype(my_array_2)).name() << std::endl;return 0;
}
- 上述函数代表类型:void()
- 可以使用函数指针指向某种函数类型,如果指向void(),函数指针应该是void(*)()
- 如果不为函数名退化为函数指针写一个Decay的特化版本,那么,传入testFunc2这个函数类型,
得到的返回类型依旧是void(),换句话说传入什么类型,就返回什么类型
#include <iostream>// 泛化版本
template<typename T>
struct RemoveReference
{using type = T;
};// 特化版本
template<typename T>
struct RemoveReference<T&>
{using type = T;
};
template<typename T>
struct RemoveReference<T&&> // 这个特化能适应 const T&&应该是伴随我一生,难以理解的噩梦了
{using type = T;
};// 泛化版本
template<typename T>
struct RemoveConst
{using type = T;
};// 特化版本
template<typename T>
struct RemoveConst<const T>
{using type = T;
};// 根据需要,可能还要增加其他特化版本
template<typename T>
using RemoveConst_t = typename RemoveConst<T>::type;template<typename T>
struct RemoveCR : RemoveConst<typename RemoveReference<T>::type>
{ // 把const和引用修饰符去掉
};template<typename T>
using RemoveCR_t = typename RemoveCR<T>::type;// Decay的trait类模板
// 泛化版本
template<typename T>
struct Decay : RemoveCR<T>
{
};// 特化版本,这个特化版本没有继承任何父类
// 有边界数组转换成指针
template<typename T,std::size_t size>
struct Decay<T[size]>
{using type = T*;
};// 无边界数组转换成指针
template<typename T>
struct Decay<T[]>
{using type = T*;
};extern int g_array[];// 简单的函数
void testFunc2()
{std::cout << "testFunc2()执行了" << std::endl;
}void rfunc()
{std::cout << "rfunc执行了" << std::endl;
}int main()
{RemoveCR_t<const int&&> rcrobj = 15; // rcrobj为int类型,只能叹为观止,惊叹rcrobj鬼斧神工地成了int类型int arr[2] = { 1,2 };Decay<decltype(arr)>::type my_array;std::cout << "my_array的类型为: " << typeid(decltype(my_array)).name() << std::endl;Decay<decltype(g_array)>::type my_array_2;std::cout << "my_array_2的类型为:" << typeid(decltype(my_array_2)).name() << std::endl;// 2Decay<decltype(testFunc2)>::type rfunc;std::cout << "rfunc类型为:" << typeid(decltype(rfunc)).name() << std::endl;rfunc();return 0;
}
现在容易理解写一个Decay特化版本把函数名(退化成)函数指针这件事了
因为函数可能有任何的返回类型以及任何数量和类型的参数,所以这个Decay的特化版本比较特殊
需要可变参模板来实现
#include <iostream>// 泛化版本
template<typename T>
struct RemoveReference
{using type = T;
};// 特化版本
template<typename T>
struct RemoveReference<T&>
{using type = T;
};
template<typename T>
struct RemoveReference<T&&> // 这个特化能适应 const T&&应该是伴随我一生,难以理解的噩梦了
{using type = T;
};// 泛化版本
template<typename T>
struct RemoveConst
{using type = T;
};// 特化版本
template<typename T>
struct RemoveConst<const T>
{using type = T;
};// 根据需要,可能还要增加其他特化版本
template<typename T>
using RemoveConst_t = typename RemoveConst<T>::type;template<typename T>
struct RemoveCR : RemoveConst<typename RemoveReference<T>::type>
{ // 把const和引用修饰符去掉
};template<typename T>
using RemoveCR_t = typename RemoveCR<T>::type;// Decay的trait类模板
// 泛化版本
template<typename T>
struct Decay : RemoveCR<T>
{
};// 特化版本,这个特化版本没有继承任何父类
// 有边界数组转换成指针
template<typename T,std::size_t size>
struct Decay<T[size]>
{using type = T*;
};// 无边界数组转换成指针
template<typename T>
struct Decay<T[]>
{using type = T*;
};extern int g_array[];// 简单的函数
void testFunc2()
{std::cout << "testFunc2()执行了" << std::endl;
}// 3
template<typename T,typename... Args>
struct Decay<T(Args...)> // 返回类型是T,参数是Args...
{using type = T(*)(Args...);
};int main()
{RemoveCR_t<const int&&> rcrobj = 15; // rcrobj为int类型,只能叹为观止,惊叹rcrobj鬼斧神工地成了int类型int arr[2] = { 1,2 };Decay<decltype(arr)>::type my_array;std::cout << "my_array的类型为: " << typeid(decltype(my_array)).name() << std::endl;Decay<decltype(g_array)>::type my_array_2;std::cout << "my_array_2的类型为:" << typeid(decltype(my_array_2)).name() << std::endl;#if 0// 2Decay<decltype(testFunc2)>::type rfunc;std::cout << "rfunc类型为:" << typeid(decltype(rfunc)).name() << std::endl;rfunc();
#endif // 3Decay<decltype(testFunc2)>::type rfunc_1;std::cout << "rfunc类型为:" << typeid(decltype(rfunc_1)).name() << std::endl;rfunc_1 = testFunc2;rfunc_1();return 0;
}
别名模板的威力
通过别名模板把Decay::type类型名简化成Decay_t
,代码如下
template<typename T>
using Decay_t = typename Decay<T>::type;
于是,main()
函数中的代码,可以写成
Decay<decltype(testFunc2)>::type rfunc;
就可以写成
Decay_t<decltype(testFunc2)> rfunc;
相关文章:

c++新经典模板与泛型编程:const修饰符的移除与增加
const修饰符的移除 让你来写移除const修饰符,你会怎么样来写? 😂😂trait类模板,如下 #include <iostream>// 泛化版本 template<typename T> struct RemoveConst {using type T; };// 特化版本 template…...

AUTOSAR汽车电子嵌入式编程精讲300篇-基于加密算法的车载CAN总线安全通信
目录 前言 研究现状 系统架构研究 异常检测研究 认证与加密研究 相关技术 2.1车联网 2.2车载网络及总线 2.2.1 CAN总线基础 2.2.2 CAN总线网络安全漏洞 2.2.3 CAN总线信息安全需求 2.3密码算法 2.3.1 AES算法 2.3.2 XTEA算法 CAN网络建模与仿真 3.1 CAN网络建模…...
4-Docker命令之docker start
1.docker start介绍 docker start命令是用来启动一个或多个已经被停止的docker容器。 2.docker start用法 docker start [参数] container [container......] [root@centos79 ~]# docker start --helpUsage: docker start [OPTIONS] CONTAINER [CONTAINER...]Start one or…...

AWS Remote Control ( Wi-Fi ) on i.MX RT1060 EVK - 2 “架构 AWS”
接续上一章节,我们把开发环境架设好之后,此章节叙述如何建立 AWS IoT 环境,请务必已经有 AWS Account,申请 AWS Account 之流程将不在此说明。 III-1. 登入AWS IoT, 在“管理”>“所有装置”>“实物”下点击“建…...

日志框架梳理(Log4j,Reload4j,JUL,JCL,SLF4J,Logback,Log4j2)
原文链接 日志框架发展历程 在了解日志框架时总会列出一系列框架:Log4j,Reload4j,JUL,JCL,SLF4J,Logback,Log4j2,这么多框架让人感到混乱,该怎么选取、该怎么用。接下来…...

内核无锁队列kfifo
文章目录 1、抛砖引玉2、内核无锁队列kfifo2.1 kfifo结构2.2 kfifo分配内存2.3 kfifo初始化2.4 kfifo释放2.5 kfifo入队列2.6 kfifo出队列2.7 kfifo的判空和判满2.8 关于内存屏障 1、抛砖引玉 昨天遇到这样一个问题,有多个生产者,多个消费者,…...

18、XSS——cookie安全
文章目录 1、cookie重要字段2、子域cookie机制3、路径cookie机制4、HttpOnly Cookie机制5、Secure Cookie机制6、本地cookie与内存cookie7、本地存储方式 一般来说,同域内浏览器中发出的任何一个请求都会带上cookie,无论请求什么资源,请求时&…...

从零开发短视频电商 Jmeter压测示例模板详解(无认证场景)
文章目录 添加线程组添加定时器添加HTTP请求默认值添加HTTP头管理添加HTTP请求添加结果断言响应断言 Response AssertionJSON断言 JSON Assertion持续时间断言 Duration Assertion 添加察看结果树添加聚合报告添加表格察看结果参考 以压测百度搜索为例 https://www.baidu.com/s…...
C++可以函数重载而C不可以的原因
函数重载是指在同一个作用域内,可以定义多个函数,它们具有相同的名称但是参数列表不同。函数重载的主要原理是函数的签名不同,而在 C 中,函数签名包括函数的名称和参数列表。而在 C 中,函数的标识仅依赖于函数的名称&a…...
Spark常见算子汇总
创建RDD 在Spark中创建RDD的方式分为三种: 从外部存储创建RDD从集合中创建RDD从其他RDD创建 textfile 调用SparkContext.textFile()方法,从外部存储中读取数据来创建 RDD parallelize 调用SparkContext 的 parallelize()方法,将一个存在的集合&…...

【华为数据之道学习笔记】3-1 基于数据特性的分类管理框架
华为根据数据特性及治理方法的不同对数据进行了分类定义:内部数据和外部数据、结构化数据和非结构化数据、元数据。其中,结构化数据又进一步划分为基础数据、主数据、事务数据、报告数据、观测数据和规则数据。 对上述数据分类的定义及特征描述。 分类维…...

电脑版便签软件怎么设置在桌面上显示?
对于不少上班族来说,如果想要在使用电脑办公的时候,随手记录一些常用的工作资料、工作注意事项等内容,直接在电脑上使用便签软件记录是比较方便的。电脑桌面便签工具不仅方便我们随时记录各类工作事项,而且支持我们快速便捷使用这…...

【华为数据之道学习笔记】2-建立企业级数据综合治理体系
数据作为一种新的生产要素,在企业构筑竞争优势的过程中起着重要作用,企业应将数据作为一种战略资产进行管理。数据从业务中产生,在IT系统中承载,要对数据进行有效治理,需要业务充分参与,IT系统确保遵从&…...
【IC前端虚拟项目】git和svn项目托管平台的简单使用说明
【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 代码托管在gitee平台上,进去后会看到文档目录“MVU芯片前端设计验证虚拟项目”和工程目录“mvu_prj”,可以通过git来下载工程: git clone gitgitee.com:gjm9999/ic_vi…...

C++ IO库
IO类 IO对象不能拷贝和赋值 iostream 表示形式的变化: 将100转换成二进制序列 然后格式化输出 x,y共用一块内存 输出的时候用不同的方式解析同一块内存 操作 格式化:内部表示转换为相应字节序列 缓存:要输出的内容放到缓存 编码转换&…...
Springboot 项目关于版本升级到 3.x ,JDK升级到17的相关问题
由于spring 停止对2.x 版本的维护,以及 jdk 频繁发布等客观因素,现需要对已有springboot 工程做一次全面升级;已因对市面上第三方等依赖库的兼容问题; 现有工程使用哥技术栈是版本: freemarker :2.3.32 spr…...

QGraphicsView实现简易地图7『异步加载-多瓦片-无底图』
前文链接:QGraphicsView实现简易地图6『异步加载-单瓦片-无底图』 前一篇文章提到的异步单瓦片加载,是指线程每准备好一个瓦片数据后,立刻抛出信号让主线程加载。而本篇异步多瓦片加载是指线程准备好所有瓦片数据后,一起抛出信号让…...
Spring Boot学习(三十三):集成kafka
前言 下面是zookeeper和kafka的官网下载地址,大家可以学习下载 zookeeper下载地址:http://zookeeper.apache.org/releases.html kafka下载地址:http://kafka.apache.org/downloads.html 1、添加依赖 在 pom.xml 文件中添加kafka依赖&am…...

MOSFET
MOSFET 电子元器件百科 文章目录 MOSFET前言一、MOSFET是什么二、MOSFET类别三、MOSFET应用实例四、MOSFET作用原理总结前言 MOSFET是一种常见的半导体器件,通过栅极电场控制通道区的导通特性,以控制电流流动。它在现代电子电路中发挥着重要的作用,并广泛应用于各种应用领域…...

DriveWorks——参数化设计非标定制利器
DriveWorks基本介绍 DriveWorks是一套被 SOLIDWORKS 认可为金牌合作伙伴产品的设计自动化软件。DriveWorks 可自动创建特定于订单的销售文档和 SOLIDWORKS 制造数据。减少重复性任务,消除错误,增加销售额,并在创纪录的时间内交付定制产品。 为…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...

Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...

Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用
Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用 Linux 内核内存管理是构成整个内核性能和系统稳定性的基础,但这一子系统结构复杂,常常有设置失败、性能展示不良、OOM 杀进程等问题。要分析这些问题,需要一套工具化、…...
iOS 项目怎么构建稳定性保障机制?一次系统性防错经验分享(含 KeyMob 工具应用)
崩溃、内存飙升、后台任务未释放、页面卡顿、日志丢失——稳定性问题,不一定会立刻崩,但一旦积累,就是“上线后救不回来的代价”。 稳定性保障不是某个工具的功能,而是一套贯穿开发、测试、上线全流程的“观测分析防范”机制。 …...