C++20 完全指南 说明
# 《C++20 完全指南》翻译说明
C++20是现代C++编程的又一次重大演进,目前最新版本的GCC、Clang和Visual C++已(部分)支持它。从C++到C++20的跨越,其意义至少与从C++到C++11的转变一样重大。C++20包含大量新的语言特性和库,这将再次改变我们使用C++编程的方式。无论是应用程序开发者,还是基础库的开发者,都将受到影响。
下图演示了C++20的主要变化,本书即对这些变化进行展开:
本书为《C++20 - The Complete Guide》一书的译本,翻译者为张小方,首发于cppguide.cn (opens new window)。
限于水平有限,如果您在阅读过程中有任何问题,可以联系小方(微信cppxiaofang),也欢迎加入我小方的C++技术交流群,群里面有很多C++高手,加群请备注“加微信群”。
小方微信公众号【CppGuide】:
# 关于本书
本书介绍了C++20所有新的语言和库特性。书中通过示例和背景信息,阐述了每个新特性的设计动机和应用场景。
和我写的其他书一样,本书重点关注新特性在实际中的应用,展示这些特性如何影响日常编程,以及你如何在项目中受益于它们。这对应用程序员和开发通用框架及基础库的程序员都适用。
# 阅读专栏前你应了解的知识
为了从本书中获得最大收获,你应该已经熟悉C++ 。你应熟悉类和引用的基本概念,并且能够使用C++标准库的组件(如输入输出流和容器)编写C++代码。你还应该熟悉现代C++的基本特性,比如auto
关键字、基于范围的for
循环,以及C++11、C++14和C++17引入的其他基本特性。
不过,你不必是专家。我的目标是让普通的C++程序员也能理解书中内容,他们不一定了解所有细节或最新特性。我会讨论基本特性,并在需要时回顾一些更细微的问题。
这样可以确保无论是专家还是中级程序员都能读懂本书。
# 本专栏的总体结构
本书涵盖了C++20引入的所有对C++的改动。这既包括语言和库特性,也包括影响日常应用编程的特性,以及用于(基础)库复杂实现的特性。不过,通常会先介绍更常见的情况和示例。
不同的章节进行了分组,但这种分组并没有更深层次的教学逻辑,只是因为先介绍的特性可能会被后续介绍的特性使用。原则上,你可以按任意顺序阅读各章节。如果不同章节的特性有结合使用的情况,书中会有相应的交叉引用。
# 如何阅读本书
不要被本书的篇幅吓到。一如既往,深入研究C++的细节时,事情可能会变得相当复杂。对于新特性的基本理解,开篇的动机阐述和示例通常就足够了。
以我的经验,学习新事物的最佳方法是查看示例。因此,你会在书中看到大量示例。有些示例只有几行代码,用于说明一个抽象概念;而另一些是完整的程序,展示了相关内容的具体应用。后一种示例会由一条C++注释引入,该注释描述了包含程序代码的文件。
# 代码实现方式
请注意以下关于我在本书中编写代码和注释的提示。
# 初始化
我通常使用现代的初始化形式(在C++11中作为统一初始化引入),在简单情况下使用花括号或=
:
int i = 42;
std::string s{ "hello "};
2
花括号初始化有以下优点:
- 它可用于基本类型、类类型、聚合类型、枚举类型和
auto
类型。 - 它可用于用多个值初始化容器。
- 它可以检测窄化错误(例如,用浮点值初始化
int
类型变量)。 - 它不会与函数声明或调用混淆。
如果花括号为空,则会调用(子)对象的默认构造函数,并且基本数据类型会被初始化为0 / false
/ nullptr
。
# 错误术语
我经常会提到编程错误。如果没有特别说明,“错误”一词或类似“... // ERROR”这样的注释,表示编译时错误。相应的代码(在符合标准的编译器上)不应编译通过。
如果我使用“运行时错误”一词,程序可能会编译通过,但运行时行为不正确或导致未定义行为(因此,它可能会也可能不会按预期运行)。
# 代码简化
我试图通过实用的示例来解释所有特性。然而,为了专注于要讲解的关键内容,我可能经常会省略代码中本应包含的其他细节。
- 大多数时候我会使用省略号(“...”)表示省略了额外的代码。注意,这里我没有使用代码字体。如果你在代码字体中看到省略号,那么代码中一定是将这三个点作为一种语言特性(比如“
typename...
”)。 - 在头文件中,我通常会省略预处理保护指令。所有头文件都应该类似如下内容:
#ifndef MYFILE_HPP
#define MYFILE_HPP
...
#endif // MYFILE_HPP
2
3
4
因此,在你的项目中使用这些头文件时请注意并修改代码。
# C++标准
C++有不同版本,由不同的C++标准定义。
最初的C++标准于1998年发布,随后在2003年通过一份技术勘误进行了修订,对原始标准进行了一些小的修正和说明。这个“旧C++标准”被称为C++98或C++03。
“现代C++”时代始于C++11,并在C++14和C++17中得到了扩展。目前,国际C++标准委员会旨在每三年发布一个新的标准。显然,这使得添加大量新内容的时间变少了,但能更快地将这些变化带给更广泛的编程社区。因此,一些大型特性的开发需要时间,可能会跨越多个标准版本。
C++20开启了下一个“更现代的C++”的发展阶段。同样,一些编程方式可能会发生变化。不过,和往常一样,编译器需要一些时间来支持最新的语言特性。在撰写本书时,主流编译器至少已经部分支持C++20。然而,和往常一样,不同编译器对新语言特性的支持程度差异很大。有些编译器可以编译本书中的大部分甚至全部代码,而另一些可能只能处理其中相当一部分代码。我预计随着各地程序员对编译器厂商提出标准支持的要求,这个问题很快就会得到解决。
# 简略目录
# 详细目录
-
1.1 <=>运算符的设计动机
- 1.1.1 C++20之前定义比较运算符
- 1.1.2 自C++20起定义比较运算符
1.2 定义和使用比较
- 1.2.1 使用<=>运算符
- 1.2.2 比较类别类型
- 1.2.3 将比较类别与<=>运算符一起使用
- 1.2.4 直接调用<=>运算符
- 1.2.5 处理多个排序标准
1.3 定义<=>运算符和==运算符
- 1.3.1 默认的==运算符和<=>运算符
- 1.3.2 默认的<=>运算符意味着默认的==运算符
- 1.3.3 默认<=>运算符的实现
1.4 重写表达式的重载决议
1.5 在泛型代码中使用<=>运算符
- 1.5.1 compare_three_way
- 1.5.2 算法lexicographical_compare_three_way()
1.6 比较运算符的兼容性问题
- 1.6.1 委托独立的比较运算符
- 1.6.2 包含受保护成员的继承
1.7 补充说明
-
2.1 普通函数参数的auto
- 2.1.1 成员函数参数的auto
2.2 在实践中使用auto作为参数
- 2.2.1 使用auto延迟类型检查
- 2.2.2 auto函数与lambda表达式
2.3 详细解析auto作为参数
- 2.3.1 auto参数的基本约束
- 2.3.2 结合模板和auto参数
2.4 补充说明
-
3.1 概念和要求的示例动机
- 3.1.1 逐步改进模板
- 3.1.2 一个使用概念的完整示例
3.2 约束和概念的适用场景
- 3.2.1 约束别名模板
- 3.2.2 约束变量模板
- 3.2.3 约束成员函数
- 3.2.4 约束非类型模板参数
3.3 概念和约束在实践中的典型应用
- 3.3.1 使用概念理解代码和错误消息
- 3.3.2 使用概念禁用泛型代码
- 3.3.3 使用要求调用不同函数
- 3.3.4 整体示例
- 3.3.5 以前的解决方法
3.4 语义约束
- 3.4.1 语义约束示例
3.5 概念的设计准则
- 3.5.1 概念应整合需求
- 3.5.2 谨慎定义概念
- 3.5.3 概念与类型特性和布尔表达式
3.6 补充说明
-
4.1 约束
4.2 requires子句
- 4.2.1 在requires子句中使用&&和||
4.3 临时布尔表达式
4.4 requires表达式
- 4.4.1 简单要求
- 4.4.2 类型要求
- 4.4.3 复合要求
- 4.4.4 嵌套要求
4.5 概念详解
- 4.5.1 定义概念
- 4.5.2 概念的特殊能力
- 4.5.3 非类型模板参数的概念
4.6 将概念用作类型约束
4.7 用概念涵盖约束
4.7.1 间接涵盖
4.7.2 定义可交换概念
-
5.1 所有标准概念概述
- 5.1.1 头文件和命名空间
- 5.1.2 标准概念涵盖关系
5.2 与语言相关的概念
- 5.2.1 算术概念
- 5.2.2 对象概念
- 5.2.3 类型之间关系的概念
- 5.2.4 比较概念
5.3 迭代器和范围的概念
- 5.3.1 范围和视图的概念
- 5.3.2 类似指针对象的概念
- 5.3.3 迭代器的概念
- 5.3.4 算法的迭代器概念
5.4 可调用对象的概念
- 5.4.1 可调用对象的基本概念
- 5.4.2 迭代器使用的可调用对象的概念
5.5 辅助概念
5.5.1 特定类型属性的概念
5.5.2 可递增类型的概念
-
6.1 通过示例了解范围和视图
- 6.1.1 将容器作为范围传递给算法
- 6.1.2范围的约束和实用工具
- 6.1.3 视图
- 6.1.4 哨兵
- 6.1.5 使用哨兵和计数定义范围
- 6.1.6 投影
- 6.1.7 实现范围相关代码的实用工具
- 6.1.8 范围的局限性和缺点
6.2 借用的迭代器和范围
- 6.2.1 借用的迭代器
- 6.2.2 借用的范围
6.3 使用视图
- 6.3.1 范围上的视图
- 6.3.2 延迟求值
- 6.3.3 视图中的缓存
- 6.3.4 过滤器的性能问题
6.4 对已销毁或修改的范围的视图
- 6.4.1 视图与其范围之间的生命周期依赖关系
- 6.4.2 具有写访问权限的视图
- 6.4.3 对变化范围的视图
- 6.4.4 复制视图可能会改变行为
6.5 视图和const
- 6.5.1 适用于容器和视图的通用代码
- 6.5.2 视图可能会消除const的传递
- 6.5.3 为视图恢复深度const属性
6.6 视图破坏的所有容器习惯用法总结
6.7 补充说明
-
7.1 将范围用作视图的关键实用工具
- 7.1.1
std::views::all()
- 7.1.2
std::views::counted()
- 7.1.3
std::views::common()
- 7.1.1
7.2 新的迭代器类别
7.3 新的迭代器和哨兵类型
- 7.3.1
std::counted_iterator
- 7.3.2
std::common_iterator
- 7.3.3
std::default_sentinel
- 7.3.4
std::unreachable_sentinel
- 7.3.5
std::move_sentinel
- 7.3.1
7.4 处理范围的新函数
- 7.4.1 处理范围(和数组)元素的函数
- 7.4.2 处理迭代器的函数
- 7.4.3 交换和移动元素/值的函数
- 7.4.4值比较的函数
7.5 处理范围的新类型函数/实用工具
- 7.5.1 范围的通用类型
- 7.5.2 迭代器的通用类型
- 7.5.3 新的函数类型
- 7.5.4 处理迭代器的其他新类型
7.6 范围算法
- 7.6.1 范围算法的优点和限制
- 7.6.2 算法概述
-
8.1 所有视图概述
- 8.1.1包装和生成视图概述
- 8.1.2适配视图概述
8.2 视图的基类和命名空间
- 8.2.1 视图的基类
- 8.2.2 为什么范围适配器/工厂有自己的命名空间
8.3 指向外部元素的源视图
- 8.3.1 子范围
- 8.3.2 引用视图
- 8.3.3 拥有视图
- 8.3.4 通用视图
8.4 生成视图
- 8.4.1 整数序列视图(Iota View)
- 8.4.2 单元素视图
- 8.4.3 空视图
- 8.4.4 输入流视图
- 8.4.5 字符串视图
- 8.4.6 跨度(Span)
8.5 过滤视图
- 8.5.1 取前视图(Take View)
- 8.5.2 取while视图(Take-While View)
- 8.5.3 丢弃视图(Drop View)
- 8.5.4 丢弃while视图(Drop-While View)
- 8.5.5 过滤视图(Filter View)
8.6 转换视图
- 8.6.1 转换视图(Transform View)
- 8.6.2 元素视图
- 8.6.3 键值视图
8.7 可变视图
- 8.7.1 反向视图
8.8 用于多个范围的视图
8.8.1 分割和延迟分割视图
8.8.2 连接视图
-
9.1 使用跨度
- 9.1.1 固定范围和动态范围
- 9.1.2 动态范围跨度的使用示例
- 9.1.3 包含非const元素的跨度使用示例
- 9.1.4 固定范围跨度的使用示例
- 9.1.5 固定范围与动态范围的比较
9.2 跨度的潜在问题
9.3 跨度的设计要点
- 9.3.1 跨度的生命周期依赖关系
- 9.3.2 跨度的性能表现
- 9.3.3 跨度的const正确性
- 9.3.4 在泛型代码中使用跨度作为参数
9.4 跨度操作
- 9.4.1 跨度操作和成员类型概述
- 9.4.2 构造函数
- 9.4.3 子跨度操作
9.5 补充说明
-
- 10.1 通过示例学习格式化输出
- 10.1.1 使用
std::format()
- 10.1.2 使用
std::format_to_n()
- 10.1.3 使用
std::format_to()
- 10.1.4 使用
std::formatted_size()
- 10.1.1 使用
- 10.2 格式化库的性能
- 10.2.1 使用
std::vformat()
和vformat_to()
- 10.2.1 使用
- 10.3 格式化输出详解
- 10.3.1 格式字符串的一般格式
- 10.3.2 标准格式说明符
- 10.3.3 宽度、精度和填充字符
- 10.3.4 格式/类型说明符
- 10.4 国际化
- 10.5 错误处理
- 10.6 用户自定义格式化输出
- 10.6.1 基本格式化器API
- 10.6.2 改进的解析功能
- 10.6.3 在用户自定义格式化器中使用标准格式化器
- 10.6.4 对字符串使用标准格式化器
- 10.7 补充说明
- 10.1 通过示例学习格式化输出
-
- 11.1 通过示例概述
- 11.1.1 每月5号安排会议
- 11.1.2 每月最后一天安排会议
- 11.1.3 每月第一个周一安排会议
- 11.1.4 使用不同时区
- 11.2 基本的chrono概念和术语
- 11.3 C++20中的chrono基本扩展
- 11.3.1 时长类型(Duration Types)
- 11.3.2 时钟(Clocks)
- 11.3.3 时间点类型(Timepoint Types)
- 11.3.4 日历类型(Calendrical Types)
- 11.3.5 时间类型hh_mm_ss
- 11.3.6 小时相关实用工具
- 11.4 chrono类型的输入/输出
- 11.4.1 默认输出格式
- 11.4.2 格式化输出
- 11.4.3 与区域设置相关的输出
- 11.4.4 格式化输入
- 11.5 在实践中使用chrono扩展
- 11.5.1 无效日期
- 11.5.2 处理月份和年份
- 11.5.3 解析时间点和时长
- 11.6 时区
- 11.6.1 时区的特点
- 11.6.2 国际标准时间数据库(IANA Timezone Database)
- 11.6.3 使用时区
- 11.6.4 处理时区缩写
- 11.6.5 自定义时区
- 11.7 时钟详解
- 11.7.1 具有指定纪元的时钟
- 11.7.2 伪时钟local_t
- 11.7.3 处理闰秒
- 11.7.4 时钟之间的转换
- 11.7.5 处理文件时钟
- 11.8 其他新的chrono特性
- 11.9 补充说明
- 11.1 通过示例概述
第12章 std::jthread和停止令牌(Stop Tokens)
- 12.1 std::jthread的设计动机
- 12.1.1 std::thread存在的问题
- 12.1.2 使用std::jthread
- 12.1.3 停止令牌和停止回调
- 12.1.4 停止令牌和条件变量
- 12.2 停止源(Stop Sources)和停止令牌
- 12.2.1 停止源和停止令牌详解
- 12.2.2 使用停止回调
- 12.2.3 停止令牌的约束和保证
- 12.3 std::jthread详解
- 12.3.1 在std::jthread中使用停止令牌
- 12.4 补充说明
- 12.1 std::jthread的设计动机
-
- 13.1 使用闩锁(Latches)和屏障(Barriers)进行线程同步
- 13.1.1 闩锁
- 13.1.2 屏障
- 13.2 信号量(Semaphores)
- 13.2.1 使用计数信号量的示例
- 13.2.2 使用二元信号量的示例
- 13.3 原子类型的扩展
- 13.3.1 使用std::atomic_ref<>实现原子引用
- 13.3.2 原子共享指针
- 13.3.3 原子浮点类型
- 13.3.4 使用原子类型进行线程同步
- 13.3.5 对std::atomic_flag的扩展
- 13.4 同步输出流
- 13.4.1 同步输出流的设计动机
- 13.4.2 使用同步输出流
- 13.4.3 将同步输出流用于文件
- 13.4.4 将同步输出流当作普通输出流使用
- 13.4.5 同步输出流在实践中的应用
- 13.5 补充说明
- 13.1 使用闩锁(Latches)和屏障(Barriers)进行线程同步
-
- 14.1 什么是协程?
- 14.2 第一个协程示例
- 14.2.1 定义协程
- 14.2.2 使用协程
- 14.2.3 引用传递时的生命周期问题
- 14.2.4 协程调用协程
- 14.2.5 实现协程接口
- 14.2.6 引导接口、句柄和承诺(Promise)
- 14.2.7 内存管理
- 14.3 产生或返回值的协程
- 14.3.1 使用co_yield
- 14.3.2 使用co_return
- 14.4 协程的可等待对象(Awaitables)和等待器(Awaiters)
- 14.4.1 等待器
- 14.4.2 标准等待器
- 14.4.3 恢复子协程
- 14.4.4 从挂起状态将值传递回协程
- 14.5 补充说明
-
- 15.1 协程约束
- 15.1.1 协程lambda表达式
- 15.2 协程帧和承诺
- 15.2.1 协程接口、承诺和可等待对象的交互方式
- 15.3 协程承诺详解
- 15.3.1 必需的承诺操作
- 15.3.2 返回或产生值的承诺操作
- 15.3.3 可选的承诺操作
- 15.4 协程句柄详解
- 15.4.1
std::coroutine_handle<void>
- 15.4.1
- 15.5 协程中的异常
- 15.6 为协程帧分配内存
- 15.6.1 协程如何分配内存
- 15.6.2 避免堆内存分配
- 15.6.3
get_return_object_on_allocation_failure()
- 15.7
co_await
和等待器详解- 15.7.1 等待器接口的详细信息
- 15.7.2 让
co_await
更新正在运行的协程 - 15.7.3 使用等待器实现对称转移以继续执行
- 15.8 处理
co_await
的其他方式- 15.8.1
await_transform()
- 15.8.2
operator co_await()
- 15.8.1
- 15.9 协程的并发使用
- 15.9.1
co_await
协程 - 15.9.2 用于协程任务的线程池
- 15.9.3 C++20之后C++库将提供的内容
- 15.9.1
- 15.10 协程特性(Coroutine Traits)
- 15.1 协程约束
-
- 16.1 通过第一个示例看模块的设计动机
- 16.1.1 实现并导出模块
- 16.1.2 编译模块单元
- 16.1.3 导入并使用模块
- 16.1.4 可达性与可见性
- 16.1.5 模块和命名空间
- 16.2 包含多个文件的模块
- 16.2.1 模块单元
- 16.2.2 使用实现单元
- 16.2.3内部分区
- 16.2.4接口分区
- 16.2.5将模块拆分为不同文件的总结
- 16.3 在实践中处理模块
- 16.3.1使用不同编译器处理模块文件
- 16.3.2处理头文件
- 16.4 模块详解
- 16.4.1 私有模块片段
- 16.4.2 模块声明和导出详解
- 16.4.3 伞状模块(Umbrella Modules)
- 16.4.4 模块导入详解
- 16.4.5 可达性与可见性符号详解
- 16.5 补充说明
- 16.1 通过第一个示例看模块的设计动机
-
- 17.1 带有模板参数的泛型lambda表达式
- 17.1.1 在实践中为泛型lambda表达式使用模板参数
- 17.1.2显式指定lambda表达式的模板参数
- 17.2 调用lambda表达式的默认构造函数
- 17.3 lambda表达式作为非类型模板参数
- 17.4
consteval
修饰的lambda表达式 - 17.5 捕获机制的变化
- 17.5.1 捕获
this
和*this
- 17.5.2 捕获结构化绑定
- 17.5.3 捕获可变参数模板的参数包
- 17.5.4 lambda表达式作为协程
- 17.5.1 捕获
- 17.6 补充说明
- 17.1 带有模板参数的泛型lambda表达式
-
- 18.1
constinit
关键字- 18.1.1 在实践中使用
constinit
- 18.1.2
constinit
如何解决静态初始化顺序问题
- 18.1.1 在实践中使用
- 18.2
consteval
关键字- 18.2.1 第一个
consteval
示例 - 18.2.2
constexpr
与consteval
的对比 - 18.2.3在实践中使用
consteval
- 18.2.4编译期值与编译期上下文
- 18.2.1 第一个
- 18.3 对
constexpr
函数的宽松约束 - 18.4
std::is_constant_evaluated()
- 18.4.1
std::is_constant_evaluated()
详解
- 18.4.1
- 18.5 在编译期使用堆内存、向量和字符串
- 18.5.1在编译期使用向量
- 18.5.2在编译期返回一个集合
- 18.5.3在编译期使用字符串
- 18.6 其他
constexpr
扩展- 18.6.1
constexpr
语言扩展 - 18.6.2
constexpr
库扩展
- 18.6.1
- 18.7 补充说明
- 18.1
-
- 19.1 非类型模板参数的新类型
- 19.1.1 浮点值作为非类型模板参数
- 19.1.2 对象作为非类型模板参数
- 19.1.3 lambda表达式作为非类型模板参数
- 19.2 补充说明
- 19.1 非类型模板参数的新类型
-
- 20.1 用于类型分类的新类型特性
- 20.1.1
is_bounded_array_v<>
和is_unbounded_array_v
- 20.1.1
- 20.2 用于类型检查的新类型特性
- 20.2.1
is_nothrow_convertible_v<>
- 20.2.1
- 20.3 用于类型转换的新类型特性
- 20.3.1
remove_cvref_t<>
- 20.3.2
unwrap_reference<>
和unwrap_ref_decay_t
- 20.3.3
common_reference<>_t
- 20.3.4
type_identity_t<>
- 20.3.1
- 20.4 用于迭代器的新类型特性
- 20.4.1
iter_difference_t<>
- 20.4.2
iter_value_t<>
- 20.4.3
iter_reference_t<>
和iter_rvalue_reference_t<>
- 20.4.1
- 20.5 用于布局兼容性的类型特性和函数
- 20.5.1
is_layout_compatible_v<>
- 20.5.2
is_pointer_interconvertible_base_of_v<>
- 20.5.3
is_corresponding_member()
- 20.5.4
is_pointer_interconvertible_with_class()
- 20.5.1
- 20.6 补充说明
- 20.1 用于类型分类的新类型特性
-
- 21.1 带初始化的范围基于
for
循环 - 21.2 使用
using
指定枚举值 - 21.3 将枚举类型委托到不同作用域
- 21.4 新字符类型
char8_t
- 21.4.1 C++标准库中针对
char8_t
的变化 - 21.4.2 破坏的向后兼容性
- 21.4.1 C++标准库中针对
- 21.5 聚合体(Aggregates)的改进
- 21.5.1 指定初始化器
- 21.5.2 用括号进行聚合体初始化
- 21.5.3 聚合体的定义
- 21.6 新的属性和属性特性
- 21.6.1
[[likely]]
和[[unlikely]]
属性 - 21.6.2
[[no_unique_address]]
属性 - 21.6.3 带参数的
[[nodiscard]]
属性
- 21.6.1
- 21.7 特性测试宏
- 21.8 补充说明
- 21.1 带初始化的范围基于
-
- 22.1 模板参数类型成员的隐式
typename
- 22.1.1 隐式
typename
的详细规则
- 22.1.1 隐式
- 22.2 泛型代码中聚合体的改进
- 22.2.1 聚合体的类模板参数推导(CTAD)
- 22.3 有条件的显式(Conditional explicit)
- 22.3.1 标准库中的有条件显式
- 22.4 补充说明
- 22.1 模板参数类型成员的隐式
-
- 23.1 字符串类型的更新
- 23.1.1 字符串成员函数
starts_with()
和ends_with()
- 23.1.2 受限的字符串成员函数
reserve()
- 23.1.1 字符串成员函数
- 23.2
std::source_location
- 23.3 整数和大小的安全比较
- 23.3.1 整数的安全比较
- 23.3.2
ssize()
- 23.4 数学常量
- 23.5 位操作实用工具
- 23.5.1 位运算
- 23.5.2
std::bit_cast<>
- 23.5.3
std::endian
- 23.6
<version>
- 23.7 算法扩展
- 23.7.1 范围支持
- 23.7.2 新算法
- 23.7.3 算法的
unseq
执行策略
- 23.8 补充说明
- 23.1 字符串类型的更新
-
- 24.1 已弃用和移除的核心语言特性
- 24.2 已弃用和移除的库特性
- 24.2.1 已弃用的库特性
- 24.2.2 已移除的库特性
- 24.3 补充说明