C++20 高级编程

文章目录

  • 前言
  • 前奏
    • lambda
    • 浅谈std::ref的实现
    • 浅谈is_same
    • 浅谈std::function的实现
    • std::visit 与 std::variant 与运行时多态
    • SFINAE
    • 类型内省
    • 标签分发 (tag dispatching)
    • 软件设计六大原则 SOLID
  • To be continue....

前言

  • C++20 是C++在C++11 之后最大的一次语言变革, 其中引入了大量具有革命性的新特性.
  • 本节包含了C++20中相当重要的四大特性: 概念约束, ranges(范围)标准库, 协程以及模块
    • 概念约束:
             是一个编译期谓词, 它根据程序员定义的接口规范对类型,常量等进行编译时检查,以便在泛型编程中为使用者提供更好的可读性与错误信息.
    • ranges标准库:
             对现有的标准库进行了补充,它以函数式编程范式进行编程, 将计算任务分解成一系列灵活的原子操作, 使得代码的正确性更容易推理.
    • 协程:
             是一种可挂起, 可恢复的通用函数, 它的切换开销是纳秒级的, 相对其他方案而言占用的资源极低, 并且可以非侵入式地为已有库扩展协程接口, 它常常用于并发编程, 生成器, 流处理, 异常处理等.
    • 模块:
             解决了传统的头文件编译模型的痛点: 依赖顺序导致头文件难以组合, 重复解析, 符号覆盖等问题, 从语言层面为程序员提供了模块化的手段.
  • 涉及了一些元编程的概念

前奏

lambda

  • lambda其实是由编译器生成的一个匿名类
  • 如果lambda包含在捕获列表内, 那么捕获将在对应的匿名类中生成成员变量与构造函数来存储捕获;
  • 对于无捕获的lambda而言, 其生成的匿名类中拥有一个非虚的函数指针类型转换操作符, 能够将lambda转换成函数指针. 这个不难理解, 因为无状态的 lambda 表达式可以赋给无状态的函数指针;
  • 在C++20 中泛型 lambda 也支持以模板参数形式提供, 这样就能保证两个形参类型一致.
    auto add=[]<typename T>(T a,T b){return a+b;};
    cout<<add(1,2)<<endl;
  • 模板函数只有实例化之后才能传递, 而泛型 lambda 是一个对象,可以按值传递, 在调用时根据实际传参进行实例化 模板函数 operator(),从而延迟了实例化的时机, 大大提高了灵活性. 标准库的一些算法通常要求对函数对象进行组合, 此时泛型 lambda 将能通过编译, 而模板函数不行.

浅谈std::ref的实现

  • 就是说构造一个新的对象(reference_wrapper是一个类模板)并在内部保存原来传入的变量的地址 _f 和类型 type
  • 重载类型转换为原来变量的引用类型, 有了_f 和 type, 这样可以在需要的时候自动将reference_wrapper转换为原来传入的变量实体的引用;
  • reference_wrapper 她本身可能会被 decay 但内部存储的 _f 值始终不会变, 始终可以在需要的时候自动转换为 type&
  • 我们可以自己简单实现一下:
    template<typename T>
    class my_ref{
    public:T * _f;explicit  my_ref(T& var): _f(addressof(var)){};operator T& () { return *_f; };
    //如果传入的是一个可调用对象template<class... Args>auto&& operator()(Args&& ...args){return (*_f)(args...);}
    };
    
  • 参考: https://zhuanlan.zhihu.com/p/581739392

浅谈is_same

  • 这是C++11引入的, 其实很简单, 模板特化就行了. 让编译器决定. 考虑: 万一我要运行时才能确定类型呢?
    template<class T, class U> struct is_same : std::false_type {};
    //偏特化版本 待确认一个模板参数
    template<class T> struct is_same<T, T> : std::true_type {};
    

浅谈std::function的实现

  • 有点类似上面的std::ref
  • 主要是对函数参数列表类型的获取, 可以使用模板特化来达到目的 C++ Template -> [5] -> 自由函数与模板可变参数

std::visit 与 std::variant 与运行时多态

  • 用法参考:
    https://zhuanlan.zhihu.com/p/676918348
    https://zhuanlan.zhihu.com/p/670189611
  • std::variant 行为像是一个类型安全的联合体。它存储了一系列类型,并能够在运行时安全地处理这些类型之一。
  • 它保留足够的空间来存储其可能的任何类型,通常是这些类型中最大者的大小。此外,std::variant 还需要额外的存储空间来跟踪当前存储的类型。
  • 为了维护类型安全,std::variant 使用一个内部索引来标记当前激活的类型。当访问或修改 std::variant 的值时,它会检查这个索引,并确保操作符合当前激活的类型。
  • std::visit 的第一个参数传入一个可调用对象,后面传入的是可调用对象的参数(可以用variant)。 std::visit 的工作原理依赖于编程语言或编译器的内部机制,这些机制通常对程序员透明。其中一种可能的实现方式是使用 “vtable”(Virtual Table,虚函数表)。 总之, std::visit 会在运行时查找函数地址。每当创建一个 std::variant 对象的时候,就会产生一个与之关联的 vtable,同来存储这个 std::variant 中存储的相关信息。
  • visit 编译时静态绑定 (第二个参数中各类型对应的第一个参数中可调用函数版本绑定到类型对应的索引 (用vtable来存储映射关系) )
  • visit 运行时动态绑定 (检查variant中当前激活的类型的索引). 从而决定要调用的函数版本
  • subtype多态例子:
    #include <iostream>
    #include <memory>
    #include <cmath>
    namespace Subtype
    {struct Shape{virtual ~Shape() = default;virtual double getArea() const = 0;virtual double getPerimeter() const = 0;};struct Circle: Shape{Circle(double r): r_(r) {}double getArea() const override{return M_PI * r_ * r_;}double getPerimeter() const override{return 2 * M_PI * r_;}private:double r_;};struct Rectangle: Shape{Rectangle(double w, double h): w_(w), h_(h) {}double getArea() const override{return w_ * h_;}double getPerimeter() const override{return 2 * (w_ + h_);}private:double w_;double h_;};
    }
    using namespace Subtype;int main(int argc, char** argv) {std::unique_ptr<Shape> shape = std::make_unique<Circle>(2);// shape area: 12.5664 perimeter: 12.5664std::cout << "shape area: " << shape->getArea()<< " perimeter: " << shape->getPerimeter() << std::endl;shape = std::make_unique<Rectangle>(2, 3);// shape area: 6 perimeter: 10std::cout << "shape area: " << shape->getArea()<< " perimeter: " << shape->getPerimeter() << std::endl;return 0;
    }
    
  • ad-hoc多态例子:
    #include <variant>
    #include <cmath>
    #include <iostream>
    namespace Adhoc
    {struct Circle{double r;};// Circle的一系列操作double getArea(const Circle& c){return M_PI * c.r * c.r;}double getPerimeter(const Circle& c){return 2 * M_PI * c.r;};struct Rectangle{double w;double h;};// Rectangle的一系列操作double getArea(const Rectangle& r){return r.w * r.h;}double getPerimeter(const Rectangle& r){return 2 * (r.w + r.h);};// 通过加法类型定义一个统一的类型Shape,其拥有不同的形状,从而实现运行时多态using Shape = std::variant<Circle, Rectangle>;// 统一类型Shape的一系列多态行为double getArea(const Shape& s){return std::visit([](const auto & data){return getArea(data);}, s);}double getPerimeter(const Shape& s){return std::visit([](const auto & data){return getPerimeter(data);}, s);};
    }
    using namespace Adhoc;int main(int argc, char** argv) {Shape shape = Circle{2};// shape area: 12.5664 perimeter: 12.5664std::cout << "shape area: " << getArea(shape)<< " perimeter: " << getPerimeter(shape) << std::endl;shape = Rectangle{2, 3};// shape area: 6 perimeter: 10std::cout << "shape area: " << getArea(shape)<< " perimeter: " << getPerimeter(shape) << std::endl;return 0;
    }
    
  • image

    subtype 多态和 ad-hoc 多态的表现形式对比

    多态形式定义多态调用
    subtype 多态Abstract* objobj->method()
    ad-hoc 多态Abstract objmethod(obj)

SFINAE

  • substitution failure is not an error

  • 典型的用法是利用enable_if
    enable_if:

    template<bool, typename _Tp = void>struct enable_if{ };// Partial specialization for true.
    template<typename _Tp>struct enable_if<true, _Tp>//第二个参数默认为void{ typedef _Tp type; };
    

    如果是false, 那么第一个空类没有type成员, 这时直接使用其type将报错
    但在SFINAE决策上下文环境中, 这种情况可以做为一种决策条件:

    下面情况下, 编译器如果根据实参推断发现enable_if<false>没有定义成员类型type, 将导致替换失败, 将其从候选集中删除, 从而达到我们的目的

    template<class T, enable_if_t<is_integral_v<T>>* = nullptr>
    void test(T t)
    {cout << "is integral" << endl;};template<class T, enable_if_t<is_floating_point_v<T>>* = nullptr>
    void test(T t)
    {cout << "is floating_point" << endl;};
    
  • Tips: 函数重载的过程中只看函数的声明, 如果它被决策为最佳可行函数, 但模板函数体内发生了模板参数替换失败, 那么就会在实例化过程中产生编译错误, 而不是SFINAE

类型内省

  • 检查对象的类型或属性的一种能力
  • 在C++中类型萃取也可以视作内省
  • 就能实现把各种类型各种分离与组合, <type_trait> 实现了这些功能
  • 比如 C++ Template -> [5] -> 自由函数与模板可变参数 中也是利用了类型内省 类似的还有数组 template<class E,size_t N> someArr<E[N]>{…}

标签分发 (tag dispatching)

  • 除了 enable_if 之外的编译时多态手段还有标签分发, 这也是C++社区著名的管用手法;

  • 标签常常是一个空类, 没有别的什么, 只是当作一种类型. 好让编译器在SFINAE决策中把它作为参考依据, 匹配出最合适的版本

  • 辅助类 true_type 和 false_type 类型也可视作标签, 他们把 true 和 false 包装成两种类型 这样在编译时就能决策出最合适的版本

  • 示例:

    template<class T>bool numEqImpl(T l, T r, true_type)
    {cout << "is floating" << endl;return true;
    }
    template<class T>bool numEqImpl(T l, T r, false_type)
    {cout << "is not floating" << endl;return false;
    }
    template<class T> enable_if_t<is_arithmetic_v<T>, bool> numEq(T l, T r)
    {return numEqImpl(l, r, is_floating_point<T> {});//标签分发
    }
    

    怎么样, 是不是逐渐理解一切了?

    标准库中在<iterator>中定义了如下迭代器标签
    input_iterator_tag;
    forward_iterator_tag;
    bidirectional_iterator_tag;
    random_access_iterator_tag;
    并且提供了配套的元函数 iterator_traits, 输入迭代器,输出相关属性;
    例如类型成员::iterator_category 存储的是迭代器的种类标签,::value_type 是解引用后的类型, ::difference_type 为迭代器作差后的类型 ptrdiff_t(通常是long类型的别名)

    iterator_traits<myIterator>::iterator_category{}实际调用时这样开始分发 ,有了标签就不用浪费内存真的去构造一个实体iterator对象了(大部分标签实现为空类)
    看看advance函数 它也利用了迭代器标签来实现

软件设计六大原则 SOLID

  • Single responsibility principle
  • Open Closed Principle
  • Liskov Substitution Principle (LSP)
  • Interface Segregation Principle
  • Dependence Inversion Principle
  • Law of Demeter
  • https://baike.baidu.com/starmap/view?nodeId=294b5d3c8cc7452ad5c9cdba&lemmaTitle=开闭原则&lemmaId=2828775&starMapFrom=lemma_starMap&fromModule=lemma_starMap

To be continue…

https://netcan.github.io/

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MongoDB:从容器使用到 Mongosh、Python/Node.js 数据操作

文章目录 1. 容器与应用之间的关系介绍2. 使用 Docker 容器安装 MongoDB3. Mongosh 操作3.1 Mongosh 连接到 MongoDB3.2 基础操作与 CRUD 4. Python 操作 MongoDB5. Nodejs 操作 MongoDB参考文献 1. 容器与应用之间的关系介绍 MongoDB 的安装有时候并不是那么容易的&#xff0…

OSI七层模型 | TCP/IP模型 | 网络和操作系统的联系 | 网络通信的宏观流程

文章目录 1.OSI七层模型2.TCP/IP五层(或四层)模型3.网络通信的宏观流程3.1.同网段通信3.2.跨网段通信 1.OSI七层模型 在计算机通信诞生之初&#xff0c;不同的厂商都生产自己的设备&#xff0c;都有自己的网络通讯标准&#xff0c;导致了不同厂家之间各种协议不兼容&#xff0…

数论Leetcode204. 计数质数、Leetcode858. 镜面反射、Leetcode952. 按公因数计算最大组件大小

Leetcode204. 计数质数 题目 给定整数 n &#xff0c;返回 所有小于非负整数 n 的质数的数量 。 代码 class Solution:def countPrimes(self, n: int) -> int:if n < 2:return 0prime_arr [1 for _ in range(n)]prime_arr[0], prime_arr[1] 0, 0ls list()for i in…

JVM基础知识汇总篇

☆* o(≧▽≦)o *☆嗨~我是小奥&#x1f379; &#x1f4c4;&#x1f4c4;&#x1f4c4;个人博客&#xff1a;小奥的博客 &#x1f4c4;&#x1f4c4;&#x1f4c4;CSDN&#xff1a;个人CSDN &#x1f4d9;&#x1f4d9;&#x1f4d9;Github&#xff1a;传送门 &#x1f4c5;&a…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-菜单管理实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…

网络原理-初识(1)

目录 网络发展史 独立模式 网络互连 局域网LAN 广域网WAN 网络通信基础 IP地址 概念 格式 端口 概念 格式 认识协议 概念 作用 五元组 网络发展史 独立模式 独立模式:计算机之间相互独立; 网络互连 随着时代的发展,越来越需要计算机之间相互通信,共享软件和数…

Springboot的 Lombok全部关联注解以及核心注解@Data详解

目录 工具安装 依赖注入 注解类别 1. Getter / Setter 2. ToString 3. EqualsAndHashCode 4. NoArgsConstructor / RequiredArgsConstructor / AllArgsConstructor 5. Data 示例 注意事项 6. Value 7. Builder 8. Slf4j / Log / Log4j / Log4j2 / XSlf4j 9. NonN…

幻兽帕鲁服务器数据备份

搭建幻兽帕鲁个人服务器&#xff0c;最近不少用户碰到内存不足、游戏坏档之类的问题。做好定时备份&#xff0c;才能轻松快速恢复游戏进度 这里讲一下如何定时将服务器数据备份到腾讯云轻量对象存储服务&#xff0c;以及如何在有需要的时候进行数据恢复。服务器中间的数据迁移…

CSS 楼梯弹弹球

<template><view class="loader"></view> </template><script></script><style>body {background-color: #212121;/* 设置背景颜色为 #212121 */}.loader {position: relative;/* 设置定位为相对定位 */width: 120px;/* 设…

java正则校验,手机号,邮箱,日期格式,时间格式,数字金额两位小数

java正则校验&#xff0c;手机号&#xff0c;邮箱&#xff0c;日期格式&#xff0c;时间格式&#xff0c;数字金额两位小数 3.58是否为金额&#xff1a;true 3.582是否为金额&#xff1a;false 1284789qq.com是否为email&#xff1a;true 1284789qq.com是否为email&#xff1…

【c语言】详解操作符(下)

前言&#xff1a; 在上文中&#xff0c;我们已经学习了 原码、反码、补码、移位 操作符、移位操作符、位操作符、逗号表达式、下标访问[ ]、函数调用&#xff08; &#xff09;&#xff0c;接下来我们将继续学习剩下的操作符。 1. 结构成员访问操作符 1.1 结构体成员的直接访…

计算机网络-ensp模拟器安装简介

一、概述 eNSP(Enterprise Network Simulation Platform)是一款由华为提供的免费的、可扩展的、图形化操作的网络仿真工具平台&#xff0c;主要对企业网路由器、交换机进行软件仿真&#xff0c;完美呈现真实设备实景&#xff0c;支持大型网络模拟。 简单来讲就是一个网络设备模…

前端大厂面试题探索编辑部——第二期

目录 题目 单选题1 题解 关于TCP 关于UDP 单选题2 题解 A选项的HTTP是否是无状态协议 B选项的HTTP支持的方法 C选项的关于HTTP的状态码 D选项HTTP协议的传输格式 题目 单选题1 1.以下哪个描述是关于 TCP 和 UDP 的区别&#xff08;&#xff09; A. TCP 是无连接的…

apipost和curl收不到服务器响应的HTTP/1.1 404 Not Found

windows的apipost发送请求后&#xff0c;服务器响应了HTTP/1.1 404 Not Found&#xff0c;但是apipost一直显示发送中。 linux上的curl也一样。 使用wireshark抓包发现收到了响应&#xff0c;但是wireshark识别不了&#xff08;图中是回应404后关闭了连接&#xff09;&#xff…

RBD —— Fracture SOP

目录 Assemble —— 清理破碎操作并生成碎片 Boolean Fracture —— 使用切割面破碎输入的几何体 Convex Decomposition —— 将输入几何体分解为凸线段 Glue Cluster —— 构建cluster值想glue约束添加强度 RBD Material Fracture —— 基于材质类型预破碎 Concrete Gl…

滑动窗口算法

长度最小的子数组(mid&#xff09; 题目链接&#xff1a;长度最小的子数组 算法思路 解法1&#xff1a;暴力枚举&#xff08;超时&#xff09;「从前往后」枚举数组中的任意⼀个元素&#xff0c;把它当成起始位置。然后从这个「起始位置开始&#xff0c;然后寻找⼀段最短的区间…

node.js 分布式锁看这篇就够用了

Redis SETNX 命令背后的原理探究 当然&#xff0c;让我们通过一个简单的例子&#xff0c;使用 Redis CLI&#xff08;命令行界面&#xff09;来模拟获取锁和释放锁的过程。 在此示例中 获取锁: # 首先&#xff0c;设置锁密钥的唯一值和过期时间(秒) 127.0.0.1:6379> SET …

工业4.0开放平台通信 统一架构OPC UA的一种测试方法

工业4.0和工业物联网&#xff08;Industrial Internet of Things, IIoT&#xff09;的核心挑战在于设备、机器以及来自不同行业服务之间的安全和标准化的数据和信息交换。 2016年11月工业4.0平台发布了指导纲要《工业4.0产品需要实现哪些准则》&#xff0c;即对于所有位于工业…

数据库:根据学校的业务规则画出E-R图以及数据库模型图,并构建一个简单的数据库

目录 序言 一、需求 二、E-R图 E-R图&#xff1a; 三、关系模式 数据库模型图&#xff1a; 四、在MYSQL中创建数据库 4.1 年级表的创建 4.2 科目表的创建 4.3 学生表的创建 4.4 成绩表的创建 结果如下&#xff1a; 序言 本篇文章我将通过一个具体的例子教会大家大家…

自定义模块加载(Python)

加载自定义模块&#xff0c;系统抛出“找不到文件”异常提示信息。 (笔记模板由python脚本于2024年01月28日 12:50:00创建&#xff0c;本篇笔记适合初通Python的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免…