【编者按】近日,C++20 标准正式公布,这不禁让人想起了 2018 年年底 C++ 标准委员会讨论 C++ 20 新特性的光景。

当时“ C++ 20 还未发布,就已凉凉?”的论调可谓火热,其中 C++ 模块化更是引起了国内外开发者的嫌弃:C++ 开发者怒了:这个无用的模块设计最终会害死 C++!

这个未出世就被群嘲“劝退”的 C++20 ,如今终于正式发布,那它带来了哪些新特性呢?一起来看大佬实战探究总结!

作者 | 连少华  责编 | 张文

头图 | CSDN 下载自视觉中国

出品 | CSDN(ID:CSDNnews)

近日,ISO C++委员会正式发布了 C++20 标准,命名为 ISO/IEC 14882:2020。

作为程序员,看到新标准发布总想尝鲜,目前 gcc 10.2 可以支持部分 C++ 20 标准,编译的时候需要使用编译选项:-std=c++2a。

Constraints and concepts (约束和概念)

在类模板和函数模板编程中,主要用于对模板参数的结束和限制,这种约束和限制发生在编译期,编译错误不再那么晦涩难懂了。
在模板编程中,可以限制模板参数的类型或具用某种特性,如:可以限制为整型、数值型、bool 型、或必须支持 hash 特性、或某个类的派生类型等。

在 C++20 中 Concepts 是非常重要的概念,模板编程终于有了质的提升。

Concepts

Concepts 是 requirements 的具名集合,concepts 需要声明在命名空间中,语法如下:

template < template-parameter-list >concept concept-name = constraint-expression;

如下所示:

template<typename T>concept Hashable = requires(T a) {    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;};//声明了一个名为Hashable的concept
struct meow {};
template<Hashable T>void f(T); // 约束这个T必须满足Hashable concept,否则无法编译通过。
int main() {  f("abc"s); // OK,string是可hash的  f(meow{}); // Error: meow结构体不是可hash的,当然可以让其支持hash。}//template<typename T>concept C=sizeof(T)>10;
template<C T>class test{};template<C T>void func(T t);

Constraints

约束是逻辑操作和操作数的序列,它用于指定对模板实参的要求。可在 requires 表达式中出现,也可直接作为 concept 的主体。

有三种类型的约束:

  • 合取(conjunction)

  • 析取(disjunction)

  • 原子约束(atomic constraint)

如下所示:

template<Incrementable T>void f(T) requires Decrementable<T>;
template<Incrementable T>void f(T) requires Decrementable<T>; // OK:重声明

Requires

requires 用于约束模板参数或具体的参数。

requires 子句

如下所示:

template<typename T>void f(T&&) requires Eq<T>; // 可作为函数声明符的最末元素出现
template<typename T> requires Addable<T> // 或在模板形参列表的右边T add(T a, T b) { return a + b; }

关键词 requires 必须后随某个常量表达式(故可以写为 requires true),但其意图是使用某个具名概念(如上例),或具名概念的一条合取/析取,或一个 requires 表达式。

表达式必须具有下列形式之一:

  • 初等表达式,例如 Swappable、std::is_integral::value、(std::is_object_v && …) 或任何带括号表达式

  • 以运算符 && 连接的初等表达式的序列

  • 以运算符 || 连接的前述表达式的序列

requires 表达式

语法如下:

requires { requirement-seq }    requires ( parameter-list(optional) ) { requirement-seq }

parameter-list - 与函数声明中类似的形参的逗号分隔列表,但不允许默认实参且不能以(并非指定包展开的)省略号结尾。这些形参无存储期、连接或生存期,它们仅用于辅助进行各个要求的制定。这些形参在要求序列的闭 } 前处于作用域中。
requirement-seq - 要求(requirement)的序列,描述于下(每个要求以分号结尾)。

requirement-seq 中的每个要求必须是下面的四项之一:

  • 简单要求(simple requirement)

  • 类型要求(type requirement)

  • 复合要求(compound requirement)

  • 嵌套要求(nested requirement)

如下所示:

template<typename T>concept Addable = requires (T x) { x + x; }; // requires 表达式
template<typename T> requires Addable<T> // requires 子句,非 requires 表达式T add(T a, T b) { return a + b; }
template<typename T>    requires requires (T x) { x + x; } // 随即的约束,注意关键字被使用两次T add(T a, T b) { return a + b; }Modules (模块)

用于从逻辑上划分代码,能够加快编译速度,并且与导入的顺序无关(还记得以前由于 #include 顺序的不同导致的编译错误吗?)
主要有三个关键字:

  • module:用于声明一个模块

  • export:用于导出模块、函数或类

  • import:用于导入模块

如下所示:
定义了一个 helloworld 模块,导出了 hello 函数

//helloworld.cppexport module helloworld;  // module declarationimport <iostream>;         // import declaration export void hello() {      // export declaration    std::cout << "Hello world!\n";}
//main.cppimport helloworld;
int main(){    hello();}Coroutines(协程)

协程,就是能够暂停执行然后在接下来的某个时间点恢复执行的函数,C++中的协程是无栈的(stack less)。使用协程可以方便的编写异步代码(和编写同步代码类似)。

主要涉及三个关键字:

  • co_await
    co_await 暂停当前协程的执行,直到等待的操作完成后继续执行。

task<> tcp_echo_server() {  char data[1024];  for (;;) {    std::size_t n = co_await socket.async_read_some(buffer(data)); #与 Python 中的 await 类似    co_await async_write(socket, buffer(data, n));  }}
上述代码,在 async_read_some() 完成后,继续执行下面的语句,在 async_read_some()执行完成之前,暂停执行并让出控制权。
  • co_yield
    co_yield 暂停执行并返回一个值,与 return 不同的是 co_yield 虽然返回了值 ,但当前函数没有终止。

generator<int> iota(int n = 0) {  while(true)    co_yield n++;  //与 Python 中的 yield 类似}
  • co_return
    co_return 用于结束当前协程的执行并返回一个值

lazy<int> f() {  co_return 7;}

当然协程也有一些限制:

  1. 不能使用变长实参;

  2. 不能使用普通的 return 语句,或占位符返回类型(auto 或 Concept);

  3. constexpr 函数、构造函数、析构函数及 main 函数不能是协程。

Ranges(范围)

提供了处理基于范围的元素(可简单理解为容器)的组件及各种适配器,还有一些新的算法。
主要有如下几类:

  • 基于范围的访问器

  • 基于范围的原语

  • 基于范围的 concept

  • 视图

  • 工厂

  • 适配器

详见头文件:

一个简单的例子:

#include <vector>#include <ranges>#include <iostream> int main(){    std::vector<int> ints{0,1,2,3,4,5};    auto even = [](int i){ return 0 == i % 2; };    auto square = [](int i) { return i * i; };     for (int i : ints | std::views::filter(even) | std::views::transform(square)) {        std::cout << i << ' ';    }}

Designated Initializers(指定初始化)

使用 {} 初始化数组、类、结构体或联合等的成员。

struct A{int a;int b;int c;};A a{.a=10,.b=100,.c=20};

operator<=>

三路比较运算符,形如:

lhs <=> rhs

其行为如下:

(a <=> b) < 0 if lhs < rhs(a <=> b) > 0 if lhs > rhs(a <=> b) == 0 if lhs equal rhs

示例如下:

#include <compare>#include <iostream>int main() {    double foo = -0.0;    double bar = 0.0;    auto res = foo <=> bar;    if (res < 0)        std::cout << "-0 is less than 0";    else if (res == 0)        std::cout << "-0 and 0 are equal";    else if (res > 0)        std::cout << "-0 is greater than 0";}

Attributes(特性)

  1. [[nodiscard( string-literal )]]:忽略返回值时警告。

  2. [[likely]] 和[[unlikely]]:指示编译器优化更可能出现的情况或分支。是一种对变量值出现可能性的一种预判。

int f(int i){    if (i < 0) [[unlikely]] {        return 0;    }     return 1;}

3. [[no_unique_address]]:用于优化存储空间,当成员为空的时候可以不占用存储空间

Others

constexpr 新增对虚函数的支持。
char8_t 用于存储utf-8的字符串。
constinit

强制常量初始化,不可以动态初始化

const char * g() { return "dynamic initialization"; }constexpr const char * f(bool p) { return p ? "constant initializer" : g(); }constinit const char * c = f(true); // OKconstinit const char * d = f(false); // error
labmda

不再支持以值的形式默认捕获参数;
允许以值的形式显示捕获 this;
支持模板,且支持可变参数;

template <typename... Args>void foo(Args... args) {  [...xs=args]{    bar(xs...); // xs is an init-capture pack  };}
std::format

使用 {} 进行格式化字符串,再也不用恶心的 stream 来拼接了,之前使用过boost 的 format,同样好用。

#include <iostream>#include <format> int main() {    std::cout << std::format("Hello {}!\n", "world");}
std::span

span 是容器的视图(即不拥有),提供对连续元素组的边界检查访问。因为视图不拥有自己的元素,所以构造和复制的成本很低;

std::jthread

新的线程类,与 std::thread 类似,只是功能更强大,支持停止、自动 join 等

Calendar 和 time zone
endian 用于判断大小端的枚举
std::make_shared 支持数组
atomic 支持浮点数和 smart ptr
std::basic_syncbuf 和 std::basic_osyncstream
string 增加 starts_with 和 end_with 函数
std::atomic_ref 原子引用
std::to_array 将 xxx 转换为 std::array
inline namespace

特性概览

核心功能特性概览



library features


更多标准功能详情,大家也可以移步至 C++ 官方发布平台查看。

  • https://en.cppreference.com/w/cpp/20

  • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2131r0.html

草案版本:

  • https://github.com/cplusplus/draft/tree/c++20

作者简介:连少华,资深架构师。钟爱 C++语言,是 C++语言的资深研究者,对新技术有敏锐洞察和见解,拥有十多年一线软件架构设计和开发经验,先后在中兴通讯、深交所和金证股份任职资深开发和架构师,同时负责软件架构的设计和核心编码。目前在互联网金融企业主导公司交易系统、行情系统和量化系统的设计与开发。先后翻译了《C++代码整洁之道》和《Python 代码整洁之道》。

更多精彩推荐
☞小米11定档12月28日;马斯克曾考虑把特斯拉卖给苹果却被拒;TiDB 4.0.9发布|极客头条
☞如果NATv6 是个笑话,那么 IPv6 是什么?☞25 岁的 JavaScript 都经历了什么?
☞最令人讨厌的编程语言:C++ Java 上榜☞Rust 2020 调查报告出炉,95%的开发者吐槽Rust难学☞从“卡脖子”到“主导”,国产数据库 40 年的演变!点分享点点赞点在看

曾被“劝退”的 C++ 20 正式发布!相关推荐

  1. 深度linux安装双,Deepin 20正式发布,新的外观和感觉,双内核安装

    经过几个月的开发,基于deepin20debian的GNU/Linux发行版现在终于可以下载了,为社区提供了一个优雅.用户友好.稳定的桌面操作系统. 基于最新的DebianGNU/Linux10.5& ...

  2. deepin20系统选择手动安装盘_Deepin 20正式发布,新的外观和感觉,双内核安装

    经过几个月的开发,基于deepin20debian的GNU/Linux发行版现在终于可以下载了,为社区提供了一个优雅.用户友好.稳定的桌面操作系统. 基于最新的DebianGNU/Linux10.5& ...

  3. 无锡deepin商店没有qq_Deepin深度操作系统20正式发布!附地址~

    继统信软件的UOS发布v20正式版之后,Deepin今天也正式推出了深度操作系统20,从界面到应用全面升级.深度OS是国内基于Linux系统的一种发行版,在国内外都有不俗影响力,在全球受欢迎程度排名1 ...

  4. GitHub 超 20000 Star,最火开源视频库 FFmpeg 这 20 年!

    [编者按]不知不觉,FFmpeg 已有 20 个年头了.这 20 年总体看来,FFmpeg 如今已非常成功,但期间也有一些波折. 项目发起人 Fabrice Bellard 曾带领部分开发团队脱离 F ...

  5. 值传递和引用传递传的到底是啥?

    作者 | 编程指北  责编 | 张文 来源 | 编程指北(ID:cs_dev) 在网上看到过很多讨论 Java.C++.Python 是值传递还是引用传递这类文章,所以这一篇呢就是想从原理讲明白关于函 ...

  6. calendR :为你定制私人专属日历

    作者 | 庄闪闪  责编 | 张文 头图 | CSDN 下载自视觉中国 来源 | 庄闪闪的成长手册(ID:Zss_R4ds) 今天教大家如何制作个人专属日历. 先说说这个包吧,非常简单,比起 ggpl ...

  7. 小米 11 不送充电器;苹果已修复 iCloud 登录激活问题;Ruby 3.0.0 发布|极客头条...

    整理 | 郑丽媛 头图 | CSDN 下载自东方 IC 快来收听极客头条音频版吧,智能播报由出门问问「魔音工坊」提供技术支持. 「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「 ...

  8. 管理大型共享数据库,做到这几点不再头疼!

    编者按:一般来说,大型共享数据库的管理会遇到不少麻烦,比如维护费用.剩余垃圾等等,但做好一些事情之后,这些麻烦就会不那么让人头疼了. 编译 | 弯月  责编 | 张文 头图 | CSDN 下载自东方 ...

  9. 前端诸神大战,Vue、React 依旧笑傲江湖

    [编者按]如今的前端框架市场基本由 React.Angular 和 Vue.js 占据.三种框架都有自己独到的优势,也有各自的缺陷.那么,项目搭建框架时该如何选择才能得到更适合的效果呢?且看本文分析. ...

最新文章

  1. git 裁切_裁切参数设计(DOE试验)
  2. 社会网络计算与社会网络分析、核心指标有哪些?社会网络计算有什么意义?
  3. 数据结构-算法: 分配排序(基数分配排序法)
  4. java session持久化_Session的生命周期和持久化
  5. 11 所允许的仓储单位类型没有针对仓储类型xxx定义
  6. arduino与风向传感器的接线_Arduino动手做(8)湿度传感器模块
  7. IO流的详细描述,字节流的文件操作,以及IO的异常描述处理,字符流的文件操作
  8. 立创EDA库导入AD18的教程
  9. cad电气工程量计算机,CAD电气管线快速算量方法
  10. 无线扩音器CE指令分析
  11. Recommending What Video to Watch Next: A Multitask Ranking System
  12. Quartus-II入门(全加器)
  13. web播放m3u8文件且进行加密处理
  14. 文科生学大数据分析吃力吗
  15. IPS(Intrusion Prevention System)入侵防护系统原理
  16. TikTok干货分享:最新基金提现步骤之W9和Paypal
  17. 数据恢复软件在苹果电脑上的应用
  18. Mac创建txt文件的两种方法
  19. 盘点5大高频移动端场景,你不会用就落后了(内附模板下载)
  20. json文件保存与读取

热门文章

  1. extjs FormPanel更改为普通表单提交,提交到iframe显示
  2. 排序算法第四篇——冒泡排序
  3. 数据结构——树状数组
  4. [USACO18JAN] Cow at Large G (dfs)
  5. bzoj3668 [Noi2014]起床困难综合症
  6. 设置 cookie过期时间
  7. 体验Visual Studio 2015 Windows Forms应用程序开发与维护
  8. Linux用树形结构显示目录结构
  9. 第3章 关系数据模型
  10. php判断ajax传入,利用PHP判断是否为AJAX请求代码