元编程

news2024/12/26 18:13:32

好久没更新,不是没学习,是不想写到CSDN而已,都写在本地了。这次是因为我觉得我对于元编程的理解还可以了,使用上还是比较少,那就得练了。

文章目录

  • 1. 类型推导工具
  • 2. type traits
    • 2.1 基本例子
    • 2.2 其他traits
  • 3. SFINAE
    • 3.1 void_t
    • 3.2 SFINAE
    • 3.3 enable_if

1. 类型推导工具

类型和值的转换

template<class T,T v>
struct integral_constant{
    static const T value = v;
    typedef T value_type;
    typedef integral_constant type;
};

T是类型,V是值。

常用的就是bool_constant,值和类型对应起来了。

template<bool,B>
using bool_constant = integral_constant<bool, B>;

typedef bool_constant<true>true_type;//<true_type能够拿到值
typedef bool_constant<false>false_type;

用途呢?值是不能用来重载的,但是类型是可以的。很多时候编译期拿到的都是值,比如is_same_v,用它可以转换成类型。

2. type traits

2.1 基本例子

判断:is_array,is_enum,is_function,is_reference,is_const,has_virtual_destructor等等

修改:decay,make_signed,remove_const,remove_cv,remove_reference等等

判断的例子:is_reference这个可以自己实现,有的实现不了。

template<class T>
struct is_reference:public false_type{};

template<class T>
struct is_reference<T&>:public true_type{};

template<class T>
struct is_refernce<T&&>:public true_type{};

template<class T>
inline constexpr bool is_reference_v = is_reference<T>::value;

修改的例子:remove_const

template<class T>
struct remove_const{
    typedef T type;
};

template<class T>
struct remove_const<const T>{
    typedef T type;
};

template<class T>
using remove_const_t = typename remove_const<T>::type

有个坑,const string &,或者const int &这种结构,remove_const后结果都还是自己,因为他们最外层是ref,即便用is_const得到的都是假,要像洋葱一样一层一层剥,最外层是referencce,再一层才是const。如果需要得到string &,需要先去掉reference,再去掉const,再加上reference。这个的主要原因是const string&应该写成string const&,我们习惯反着写。
gpt给的例子

#include <bits/stdc++.h>

#include <iostream>
#include <type_traits>
template <typename T>
void remove_const_example() {
  std::cout << std::is_reference<T>::value << std::endl;
  std::cout << std::is_const<T>::value << " -> "  // 输出是否是 const 类型
            << std::is_const<typename std::remove_const<T>::type>::value
            << std::endl;  // 输出移除 const 后的类型是否是 const
}

int main() {
  remove_const_example<const int&>();
  // remove_const_example<const int>();  // 输出: 1 -> 0
  // remove_const_example<int>();        // 输出: 0 -> 0
  // remove_const_example<const double>(); // 输出: 1 -> 0
  return 0;
}

2.2 其他traits

吴老师举了几个他常用的例子

is_pod的traits不建议用了,最新的c++已经没有了,因为这一般不是我们想要的,用下面两个替换掉了

  • is_standard_layout:内存使用标准布局,其实就是没有虚函数、没有继承、没有static函数这些。

  • is_trivial:默认构造、复制、析构是不是不需要做任何特殊处理。分为下面两种

    • is_trivially_default_constructible:默认构造不需要执行任何动作
    • is_trivially_copyable:复制时可以简单复制内存块,不需要做特殊处理。这个还分为下面四种
      • is_trivially_copy_constructible:拷贝构造可以简单复制内存块
      • is_trivially_copy_assignable:拷贝赋值可以复制简单内存块
      • is_trivially_move_constructible:移动构造可以复制简单内存块
      • is_trivially_mvoe_assignable:移动赋值可以复制简单内存块
      • is_trivially_destructible:析构不需要执行任何操作

3. SFINAE

3.1 void_t

如果变参模板中能够正常匹配就去掉全部类型,只剩void,如果不能,就直接失败(SFINAE)。

template<typename...>
using void_t=void;

3.2 SFINAE

用void_t实现has_reserve

#include <bits/stdc++.h>

template <typename T, typename C= void>
struct has_reserve : std::false_type {
  has_reserve() { std::cout << "not" << std::endl; }
};

template <typename T>
struct has_reserve<T, std::void_t<decltype(std::declval<T&>().reserve(1U))>>
    : std::true_type {
  has_reserve() { std::cout << "has" << std::endl; }
};
struct A {};
struct B {
  void reserve(unsigned int) {}
};
int main() {
  has_reserve<A> a;
  has_reserve<B> b;
}

我之前为什么一直没理解SFINAE,其实主要是没理解默认参数的特化。有几个核心点

  1. 为什么主模板要写个默认参数?
  • 先说答案,不写默认的,就写个typename = void也是可以的(其实这个更好),原因在下面。
  • 这个默认参数也不是必须是void,但是为了要用下面的std::void_t就只能用void,我尝试写了一个int_t,也是可以的。
  • 特化版本的第二个如果能够满足里面的条件,就变成了has_reserve<T,void>。前面说到void是默认参数,或者说正常情况是没人给has_reserve传第二个参数的,也就是说第二个参数必须是void。而特化版本第二个也必须是void才能匹配上主模板。还是那句话,主模板是void不是因为自己,是因为特化需要void_t。
  1. 如果主模板的默认参数是int,其它不变会怎么样?

特化会依然是它的特化,但是不会被匹配到。还是那句话,没人会传入第二个参数,就是说,你写的 has_reserve<A> a;会变成 has_reserve<A,int> a;,怎么可能和 has_reserve<T,void>匹配呢?

如果你非要说,我就这么写has_reserve<A,void> a;,那好,你能匹配上。

  1. 特化std::void_t里面是什么呢?

这里面确实有点乱,但还不至于恶心到看不懂。std::declval<T&>()用于在编译期构造一个对象,这个东西我搜来搜去基本都用来检查是否有某个函数了。cppreference上说哪怕它的构造不能调,也可以这么写,因为只有编译阶段生效。注意declval前面一定要加std。

3.3 enable_if

先说enable_if的实现。第一个是参数是true,type就返回T,不是,就不满足sfinae标准,匹配失败。

template<bool B, class T = void>
struct enable_if {};
 
template<class T>
struct enable_if<true, T> { typedef T type; };

上面的扩展

#include <bits/stdc++.h>

template <typename T, typename = void>
struct has_reserve : std::false_type {
  has_reserve() { std::cout << "not" << std::endl; }
};

template <typename T>
struct has_reserve<T, std::void_t<decltype(std::declval<T&>().reserve(1U))>>
    : std::true_type {
  has_reserve() { std::cout << "has" << std::endl; }
};

template <typename C>
std::enable_if_t<has_reserve<C>::value, void> put_data(C& container) {
  std::cout << "has func" << std::endl;
}

template <typename C>
std::enable_if_t<!has_reserve<C>::value, void> put_data(C& container) {
  std::cout << "not" << std::endl;
}
struct A {};
struct B {
  void reserve(unsigned int) {}
};
int main() {
  has_reserve<A> a;
  has_reserve<B, void> b;
  std::vector<int> c;
  std::list<int> d;
  put_data(c);
  put_data(d);
}

首先,我把主模板的默认参数改掉了。这种写法相当于把enable加到了返回值上。回到enable_if的定义,如果第一个value失败了就SFINAE失败,换下一个,如果是真的就直接返回第二个参数:void。如果函数需要有返回值那就把enable_if后面的void换成其它类型,比如int。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2254080.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kubernetes架构原则和对象设计

云原生学习路线导航页&#xff08;持续更新中&#xff09; 快捷链接 Kubernetes常见问题解答 本文从 Google Borg系统的架构设计开始&#xff0c;深入讲解Kubernetes架构及组件的基本原理 1.什么是云计算 1.1.传统行业应用 假设有10台服务器&#xff0c;两个应用。小规模管…

力扣-图论-1【算法学习day.51】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…

学习笔记056——Docker日志的清理问题

文章目录 Docker日志的清理问题1、Docke日志所在位置2、日志清理 Docker日志的清理问题 Ubuntu上部署Docker&#xff0c;运行一段时间后&#xff0c;会累计很多的日志量。 如果不及时处理&#xff0c;会占用系统空间&#xff0c;影响系统性能。 如何处理日志累计过大的问题&…

Python3:Pytest框架parametrize报错in “parametrize“ the number of names (4)

Python3&#xff1a;Pytest框架parametrize报错in “parametrize“ the number of names (4) 排查原因&#xff1a;是pytest入参时&#xff0c;需要4个参数&#xff0c;但是提供了3个参数 test_tenant_list:- ["http://xx:8081/scheduler/v1/tenancy/list",{"co…

Linux 35.6 + JetPack v5.1.4之RTP实时视频Python框架

Linux 35.6 JetPack v5.1.4之RTP实时视频Python框架 1. 源由2. 思路3. 方法论3.1 扩展思考 - 慎谋而后定3.2 扩展思考 - 拒绝拖延或犹豫3.3 扩展思考 - 哲学思考3.4 逻辑实操 - 方法论 4 准备5. 分析5.1 gst-launch-1.05.1.1 xvimagesink5.1.2 nv3dsink5.1.3 nv3dsink sync05…

GIt (一) Git的安装,项目搭建,远程仓库,分支

文章目录 一、 版本控制1.1 集中式版本控制1.2 分布式版本控制 二、 Git的安装及配置2.1 安装2.2 Git的配置2.2 查看配置 三、 Git基本理论3.1 工作区域3.2 文件状态 四、Git项目的搭建与操作4.1 初始化Git仓库4.2 常见的操作4.2.1 文件添加到暂存区4.2.2 文件提交更新4.2.3 查…

iview upload clearFiles清除回显视图

iview upload 上传完文件之后清除内容&#xff0c;打开会回显视图&#xff0c;清除不掉 关闭弹框时主动清除回显内容即可this.$refs.uploads.clearFiles() <FormItem label"上传附件:" :label-width"formNameWidth"><Upload action"/fms/ap…

JAVA |日常开发中Servlet详解

JAVA &#xff5c;日常开发中Servlet详解 前言一、Servlet 概述1.1 定义1.2 历史背景 二、Servlet 的生命周期2.1 加载和实例化2.2 初始化&#xff08;init 方法&#xff09;2.3 服务&#xff08;service 方法&#xff09;2.4 销毁&#xff08;destroy 方法&#xff09; 三、Se…

【C++】入门【六】

本节目标 一、继承的概念及定义 二、基类和派生类对象赋值转换 三、继承中的作用域 四、派生类的默认成员函数 五、继承与友元 六、继承与静态成员 七、复杂的菱形继承及菱形虚拟继承 八、继承的总结和反思 九、笔试面试题 一、继承的概念及定义 1.继承的概念 继承是面向对象…

Docker--Docker Image(镜像)

什么是Docker Image&#xff1f; Docker镜像&#xff08;Docker Image&#xff09;是Docker容器技术的核心组件之一&#xff0c;它包含了运行应用程序所需的所有依赖、库、代码、运行时环境以及配置文件等。 简单来说&#xff0c;Docker镜像是一个轻量级、可执行的软件包&…

架构05-架构安全性

零、文章目录 架构05-架构安全性 1、软件架构安全的重要性 **系统安全&#xff1a;**不仅包括防御黑客攻击&#xff0c;还包括安全备份与恢复、安全审计、防治病毒等。**关注重点&#xff1a;**认证、授权、凭证、保密、传输安全、验证。 2、认证&#xff08;Authenticatio…

elasticsearch-如何给文档新增/更新的字段

文章目录 前言elasticsearch-如何给文档新增/更新的字段1. 如何给某些文档新增/更新的字段2. 给所有文档添加/更新一个新的字段3. 测试 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且…

Ansible自动化一键部署单节点集群架构

自动化部署利器&#xff1a;Ansible 一键部署脚本 在现代IT基础设施管理中&#xff0c;Ansible以其简洁、强大的自动化能力脱颖而出。以下是精心打造的Ansible自动化一键部署脚本&#xff0c;旨在简化部署流程&#xff0c;提升效率&#xff0c;确保一致性和可靠性。 通过这个…

常见Linux命令(详解)

文章目录 常见Linux命令文件目录类命令pwd 打印当前目录的绝对路径ls 列出目录内容cd 切换路径mkdir 建立目录rmdir 删除目录touch 创建空文件cp 复制文件或目录rm 移除文件或者目录mv 移动文件与目录或重命名cat 查看文件内容more 文件分屏查看器less 分屏显示文件内容head 显…

数据库复习记录

边复习边整理。 数据库 数据库管理系统&#xff08;DBMS&#xff09;&#xff1a;用来管理数据库的系统。 关系型数据库&#xff1a;二维表格&#xff08;即关系&#xff09;来存储数据&#xff0c;一个表对应一个关系&#xff0c;用SQL来查询数据。如MySQL、PostgreSQL、SQ…

Navicat连接SQL Server及SpringBoot连接SQL Server(jtds)

Navicat连接SQL Server 安装自带的SQL Server客户端 去到Navicat安装目录&#xff0c;找到安装程序&#xff0c;安装即可。 安装对应版本的Microsoft ODBC Driver for SQL Server 打开Navicat输入对应的SQL Server相关信息 然后点测试连接&#xff0c;提示连接成功。 Spr…

数字图像处理(15):图像灰度反转和彩色反转

&#xff08;1&#xff09;图像反转&#xff1a;是指对图像的颜色信息进行相反的处理&#xff0c;从而得到一个新的图像。在计算机视觉和图像处理领域&#xff0c;图像反转是一种常见的操作&#xff0c;它可以帮助我们实现不同的图像特效和视觉效果。 &#xff08;2&#xff09…

Linux——基础命令(3)

1.Linux——基础命令&#xff08;1&#xff09;-CSDN博客 2.Linux——基础命令&#xff08;2&#xff09; 文件内容操作-CSDN博客 一、打包压缩 打包压缩 是日常工作中备份文件的一种方式 在不同操作系统中&#xff0c;常用的打包压缩方式是不同的选项 含义 Windows 常用 rar…

【力扣热题100】—— Day4.回文链表

正视自己的懦弱和无能&#xff0c;克服自己的嫉妒与不甘 —— 24.12.3 234. 回文链表 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为 回文链表 。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a…

python源码实例游戏开发小程序办公自动化网络爬虫项目开发源码(250+个项目、26.6GB)

文章目录 源代码下载地址项目介绍预览 项目备注源代码下载地址 源代码下载地址 点击这里下载源码 项目介绍 python源码实例游戏开发小程序办公自动化网络爬虫项目开发源码(250个项目、26.6GB) 预览 项目备注 1、该资源内项目代码都经过测试运行成功&#xff0c;功能ok的情…