当前位置: 首页 > news >正文

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;
}

退化技术

  1. 某些类型一旦传递给函数模板(通过函数模板来推断相关的类型),那么推断出来的类型就会产生退化。所谓退化(decay),就是把类型中的一些修饰符丢弃了。例如,const int中的const丢弃后,就变成int类型,那么对于const int类型,int类型就是一种退化的表现。
  2. 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;
}

在这里插入图片描述

  1. 上述函数代表类型:void()
  2. 可以使用函数指针指向某种函数类型,如果指向void(),函数指针应该是void(*)()
  3. 如果不为函数名退化为函数指针写一个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修饰符&#xff0c;你会怎么样来写&#xff1f; &#x1f602;&#x1f602;trait类模板&#xff0c;如下 #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”

接续上一章节&#xff0c;我们把开发环境架设好之后&#xff0c;此章节叙述如何建立 AWS IoT 环境&#xff0c;请务必已经有 AWS Account&#xff0c;申请 AWS Account 之流程将不在此说明。 III-1. 登入AWS IoT&#xff0c; 在“管理”>“所有装置”>“实物”下点击“建…...

日志框架梳理(Log4j,Reload4j,JUL,JCL,SLF4J,Logback,Log4j2)

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

内核无锁队列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、抛砖引玉 昨天遇到这样一个问题&#xff0c;有多个生产者&#xff0c;多个消费者&#xff0c…...

18、XSS——cookie安全

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

从零开发短视频电商 Jmeter压测示例模板详解(无认证场景)

文章目录 添加线程组添加定时器添加HTTP请求默认值添加HTTP头管理添加HTTP请求添加结果断言响应断言 Response AssertionJSON断言 JSON Assertion持续时间断言 Duration Assertion 添加察看结果树添加聚合报告添加表格察看结果参考 以压测百度搜索为例 https://www.baidu.com/s…...

C++可以函数重载而C不可以的原因

函数重载是指在同一个作用域内&#xff0c;可以定义多个函数&#xff0c;它们具有相同的名称但是参数列表不同。函数重载的主要原理是函数的签名不同&#xff0c;而在 C 中&#xff0c;函数签名包括函数的名称和参数列表。而在 C 中&#xff0c;函数的标识仅依赖于函数的名称&a…...

Spark常见算子汇总

创建RDD 在Spark中创建RDD的方式分为三种: 从外部存储创建RDD从集合中创建RDD从其他RDD创建 textfile 调用SparkContext.textFile()方法&#xff0c;从外部存储中读取数据来创建 RDD parallelize 调用SparkContext 的 parallelize()方法&#xff0c;将一个存在的集合&…...

【华为数据之道学习笔记】3-1 基于数据特性的分类管理框架

华为根据数据特性及治理方法的不同对数据进行了分类定义&#xff1a;内部数据和外部数据、结构化数据和非结构化数据、元数据。其中&#xff0c;结构化数据又进一步划分为基础数据、主数据、事务数据、报告数据、观测数据和规则数据。 对上述数据分类的定义及特征描述。 分类维…...

电脑版便签软件怎么设置在桌面上显示?

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

【华为数据之道学习笔记】2-建立企业级数据综合治理体系

数据作为一种新的生产要素&#xff0c;在企业构筑竞争优势的过程中起着重要作用&#xff0c;企业应将数据作为一种战略资产进行管理。数据从业务中产生&#xff0c;在IT系统中承载&#xff0c;要对数据进行有效治理&#xff0c;需要业务充分参与&#xff0c;IT系统确保遵从&…...

【IC前端虚拟项目】git和svn项目托管平台的简单使用说明

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 代码托管在gitee平台上&#xff0c;进去后会看到文档目录“MVU芯片前端设计验证虚拟项目”和工程目录“mvu_prj”&#xff0c;可以通过git来下载工程&#xff1a; git clone gitgitee.com:gjm9999/ic_vi…...

C++ IO库

IO类 IO对象不能拷贝和赋值 iostream 表示形式的变化&#xff1a; 将100转换成二进制序列 然后格式化输出 x,y共用一块内存 输出的时候用不同的方式解析同一块内存 操作 格式化&#xff1a;内部表示转换为相应字节序列 缓存&#xff1a;要输出的内容放到缓存 编码转换&…...

Springboot 项目关于版本升级到 3.x ,JDK升级到17的相关问题

由于spring 停止对2.x 版本的维护&#xff0c;以及 jdk 频繁发布等客观因素&#xff0c;现需要对已有springboot 工程做一次全面升级&#xff1b;已因对市面上第三方等依赖库的兼容问题&#xff1b; 现有工程使用哥技术栈是版本&#xff1a; freemarker &#xff1a;2.3.32 spr…...

QGraphicsView实现简易地图7『异步加载-多瓦片-无底图』

前文链接&#xff1a;QGraphicsView实现简易地图6『异步加载-单瓦片-无底图』 前一篇文章提到的异步单瓦片加载&#xff0c;是指线程每准备好一个瓦片数据后&#xff0c;立刻抛出信号让主线程加载。而本篇异步多瓦片加载是指线程准备好所有瓦片数据后&#xff0c;一起抛出信号让…...

Spring Boot学习(三十三):集成kafka

前言 下面是zookeeper和kafka的官网下载地址&#xff0c;大家可以学习下载 zookeeper下载地址&#xff1a;http://zookeeper.apache.org/releases.html kafka下载地址&#xff1a;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 制造数据。减少重复性任务&#xff0c;消除错误&#xff0c;增加销售额&#xff0c;并在创纪录的时间内交付定制产品。 为…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...