第 11 章 C++23
如果说 C++20 是继 C++11 之后又一次「大版本」式的更新,那么 C++23 更像是一次以巩固与完善为主的增量更新:它补全了 C++20 中一些仓促引入的特性,并加入了大量呼声已久的库设施。本章我们挑选其中较为重要、也较为实用的特性进行介绍。
本章的示例使用了 C++23 的特性,编译时需要使用
-std=c++2b(或-std=c++23)。此外,受限于各标准库实现的进度,部分库特性(如std::generator、std::move_only_function、<stacktrace>、import std;)可能在你的工具链上尚不可用,本章会在相应位置加以说明。
11.1 语言特性
显式对象形参(Deducing this)
在 C++23 之前,如果我们想让一个成员函数同时适配 const/非 const、左值/右值等多种情形,往往不得不书写多个几乎完全重复的重载。C++23 引入了**显式对象形参 (explicit object parameter)**,允许把「this」写成函数的第一个形参,并通过模板对其 cv 与引用限定进行推导,从而用一个函数模板覆盖所有情形:
struct Counter { |
这一特性还使得「递归 lambda」等以往十分别扭的写法变得自然。
if consteval
C++23 引入了 if consteval,用于在函数体内区分当前是否处于常量求值的语境,从而为编译期与运行期分别选择不同的实现:
constexpr int compute(int x) { |
多维下标运算符
C++23 允许 operator[] 接受多个下标参数,使得多维容器可以使用直观的 m[i, j] 语法,而不必退而求其次地使用 operator():
struct Matrix2x3 { |
auto(x) 与静态 operator()
C++23 提供了 auto(x) / auto{x} 语法来显式地产生一份**退化拷贝 (decay-copy)**,即一个与 x 同类型的纯右值副本,这在需要明确「我要的是一份副本」时非常有用:
std::vector<int> v{1, 2, 3}; |
此外,函数对象(包括 lambda)的调用运算符现在可以声明为 static,省去了隐式对象形参,在某些场景下能带来更好的性能:
struct Add { |
[[assume]]
[[assume(expr)]] 是 C++23 标准化的属性,用于向编译器声明某个表达式在此处一定为真,从而允许优化器据此进行优化。需要强调的是,如果该假设在运行期实际并不成立,将导致未定义行为,因此应当谨慎使用:
int divide_by(int x) { |
其他语言层面的小改进
C++23 还包含若干较小的语言改进:
- 预处理指令:新增了
#elifdef/#elifndef(即#elif defined/#elif !defined的简写)以及用于主动产生诊断信息的#warning。 - 命名的通用字符转义:可以用字符的 Unicode 名称来书写字符,例如
"\N{GREEK SMALL LETTER ALPHA}",比记忆码位更直观。 - 扩展浮点类型:在
<stdfloat>中引入了std::float16_t、std::float32_t、std::float64_t、std::bfloat16_t等定宽浮点类型(其可用性同样取决于平台与标准库实现)。 constexpr的进一步放宽:允许在常量求值中使用goto、带标签的语句,以及声明(但不在常量求值期间使用)非字面类型的变量与static变量等。
11.2 标准库
std::expected
std::expected<T, E> 表示一个「要么是值 T、要么是错误 E」的结果,为不依赖异常的错误处理提供了类型安全、表达力强的手段,与其他语言中的 Result 类型类似:
#include <expected> |
std::print 与 std::println
C++23 的 <print> 提供了 std::print 与 std::println,它们基于 C++20 的 std::format,以类型安全的格式化字符串进行输出,比传统的 iostream 链式 << 更简洁、也更高效:
#include <print> |
std::mdspan
std::mdspan 是对一段连续存储的非拥有多维视图。它本身不分配内存,只是按照给定的维度(extents)重新解读底层一维数组的布局,常用于科学计算与高性能计算:
#include <mdspan> |
std::flat_map 与 std::flat_set
std::flat_map / std::flat_set 是以有序连续存储(默认由两个 vector 实现)为底层的关联容器。相比基于红黑树的 std::map,它们的插入较慢,但查找与遍历对缓存更友好、内存开销也更小:
#include <flat_map> |
范围库的增强
C++23 为范围库补充了大量实用的适配器,例如 views::zip(将多个范围按位置「拉链」在一起逐元素并行遍历)、views::enumerate(为元素附上下标)、views::chunk、views::slide、views::join_with 等,以及通用的 std::ranges::to(把一个范围物化为具体容器):
#include <ranges> |
其他改进
C++23 还包含许多零散但实用的小改进,例如:
std::string/std::string_view新增了contains成员,可直接判断是否包含某个子串或字符;<bit>中新增了std::byteswap,用于反转整数的字节序;std::optional增加了and_then、transform、or_else等单子式(monadic)操作。
11.3 关于标准库支持情况
C++23 的语言特性大多已被主流编译器较好地支持,但部分库特性的落地进度因实现而异。例如 std::generator(协程范围)、std::move_only_function、<stacktrace> 以及标准库模块 import std; 在某些标准库(尤其是 libc++)中可能尚未实现或仍处于实验阶段。在使用这些特性前,建议先查阅 cppreference 的编译器支持页面 确认你所用工具链的支持情况。
总结
C++23 虽然不像 C++11 或 C++20 那样带来颠覆性的变化,但 std::expected、std::print、std::mdspan、显式对象形参等特性,实实在在地改善了日常编写 C++ 的体验。结合后续的 C++26,现代 C++ 仍在持续演进。
进一步阅读的参考资料
欧长坤 © 2016-2026 版权所有, 采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议许可,代码使用 MIT 协议开源。
如果你认为本书对你起到了帮助,可以资助作者。