一、概述
C++20(ISO/IEC 14882:2020)被誉为自 C++11 以来最大、最激进的一次升级。一次性交付了社区讨论了 20 年的 Concepts 概念、Ranges 范围库、Coroutines 协程、Modules 模块化"四大天王",外加 三路比较飞船运算符 <=>、consteval/constinit、std::format 格式化库、std::ranges 算法 / view 管道、jthread/stop_token、calendar 时区库、std::span、designated initializer 等几十个重磅特性。整体效果:写 C++20 的代码和写现代 Python / Rust 的体验已经差别不大。
目录
- Concepts 概念 + requires(模板约束的终极答案)
- Ranges 库:范围 / 视图 / 管道运算符 |
- Coroutines 协程:co_await / co_yield / co_return
- Modules 模块化:import / export / module
- 三路比较 <=>(飞船运算符)与自动 == 生成
- consteval / constinit / requires 表达式
- [[likely]]/[[unlikely]] · char8_t · 指定初始化器
- std::format 现代格式化库(printf/iostream 终结者)
- std::span 连续视图(零拷贝数组/vector 通用)
- std::source_location + std::erase_if + std::atomic_ref
- jthread / stop_token(RAII 线程 + 停止令牌)
- 日历 / 时区 chrono 升级(真正能用的时间 API)
二、C++20 四大天王(改变 C++ 写法的核心特性)
2.1 Concepts 概念约束模板参数
模板革命过去写模板约束只能用 SFINAE + type_traits + enable_if,代码像黑魔法、编译器报错是 50 页的天书。Concepts 把"类型应满足什么条件"提炼成具名谓词:template<Incr T> void f(T x);配套的 requires 表达式可直观写出"这个类型要有 ++、要能 << 到 ostream"等操作约束。编译错误从此变成一行:模板参数 T 不满足 Concept Xxx。
#include <concepts>
#include <type_traits>
#include <iostream>
#include <string>
// (1) 自定义一个 Concept:必须是数字类型 + 可加减 + 能转 double
template<typename T>
concept Arithmetic = std::is_arithmetic_v<T> &&
requires(T a, T b) {
{ a + b } -> std::convertible_to<double>;
{ a - b } -> std::convertible_to<double>;
};
// (2) 把 Concept 作为模板形参"类型约束"使用(4 种等价语法)
/* 语法 A:最直观 — 把 concept 写在 typename 的位置 */
template<Arithmetic T>
T sum1(T x, T y) { return x + y; }
/* 语法 B:requires 子句(可 && 多个约束、更灵活组合) */
template<typename T>
requires Arithmetic<T> && std::integral<T>
T sum2(T x, T y) { return x + y; }
/* 语法 C:尾部 requires(约束更复杂时推荐) */
template<typename T>
T sum3(T x, T y) requires Arithmetic<T> { return x + y; }
/* 语法 D:缩写函数模板(auto 约束) */
Arithmetic auto sum4(Arithmetic auto x, Arithmetic auto y) { return x + y; }
/* (3) 标准库内建 Concepts 开箱即用 */
#include <iterator>
template<std::input_iterator It, typename T>
It myFind(It first, It last, const T& val) {
for (; first != last; ++first) if (*first == val) break;
return first;
}
/* 传入错误类型会立即给出 Concept 级报错(而不是 50 页 SFINAE 堆栈!)
* e.g. 传一个 int 代替 iterator → 编译器:
* "note: constraints not satisfied ... because 'std::input_iterator' evaluated to false"
*/
2.2 Ranges 范围库:视图 / 算法 / 管道 |
范式升级把 STL 算法从"(begin, end)两迭代器"模式升级为"range 整体"模式,算法第一个参数直接传容器即可。更重要的是 Views 视图 + 管道运算符 |:vec | filter(pred) | transform(f) | reverse | take(5),链式组合、惰性求值、零中间容器分配,和 Rust/Linq/Dplyr 的体验完全一致。
#include <ranges>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
namespace rng = std::ranges;
namespace vws = std::views;
struct Employee {
std::string name;
int age;
double salary;
int dept;
};
int main() {
std::vector<Employee> staff = {
{"Alice", 30, 18000, 1},
{"Bob", 45, 35000, 1},
{"Carol", 28, 16000, 2},
{"David", 38, 24000, 2},
{"Echo", 52, 42000, 1},
};
/* 【1】Ranges 算法:直接传容器,不再写 begin/end */
rng::sort(staff, &Employee::age); // 按年龄升序;还能投影到成员(C++20 projections)
bool ok = rng::all_of(staff, [](auto& e){ return e.age >= 18; }); // 直接 all_of 容器
/* 【2】Views 管道:惰性计算 + 零中间容器(像 Rust 迭代器适配器 / C# LINQ)
取 dept==1 的高薪员工 → 映射到姓名 → 全部大写 → 取前 2 个 → 打印 */
auto topNames =
staff
| vws::filter( [](auto& e){ return e.dept == 1; })
| vws::filter( [](auto& e){ return e.salary > 20000; })
| vws::transform(&Employee::name)
| vws::transform([](auto&& s){ return s | vws::transform(::toupper) | rng::to<std::string>(); })
| vws::take(2);
// 直到 for 循环访问,整条管道才真正运行(惰性)
for (auto&& name : topNames) std::cout << name << "\n"; // → BOB, ECHO
/* 【3】常用视图:iota(连续整数)、keys/values(map 列)、join(展平)、split */
auto squares = vws::iota(1, 11) // 1..10
| vws::transform([](int i){ return i * i; })
| rng::to<std::vector<int>>(); // 到 C++23 的 to();C++20 可用 copy 替代
}
2.3 Coroutines 协程(co_await / co_yield / co_return)
并发基础C++20 第一次把"可暂停/可恢复的函数"标准化为语言级协程。三大关键字:co_await(挂起等待某个任务)、co_yield(产出一个值并挂起,下次 resume 从这里继续)、co_return(返回最终值结束)。协程不绑定特定运行时(库需要自己提供 Promise / Awaitable 类型),C++23 开始补充 std::generator,当前示例是一个最常见的惰性"生成器"。
/* 最简生成器(coroutine output iterator 方案):C++20 没有官方 generator,这里手写最小可用版本 */
#include <coroutine>
#include <memory>
#include <optional>
#include <iostream>
template<typename T>
struct Generator {
struct promise_type;
using Handle = std::coroutine_handle<promise_type>;
struct promise_type {
std::optional<T> current;
Generator get_return_object() { return Generator{Handle::from_promise(*this)}; }
auto initial_suspend() { return std::suspend_always{}; } // 起始即挂起:lazy
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() { throw; }
template<typename U>
auto yield_value(U&& v) { // co_yield v 时调用
current.emplace(std::forward<U>(v));
return std::suspend_always{};
}
void return_void() {}
};
Handle coro_;
explicit Generator(Handle h) : coro_(h) {}
~Generator() { if (coro_) coro_.destroy(); }
// 迭代器协议:可直接用 range-for 遍历
bool next() { coro_.resume(); return !coro_.done(); }
T& value() { return *coro_.promise().current; }
};
// ✨ co_yield 生成无限斐波那契数列:状态完全由协程帧保留,无需手写循环变量
Generator<long long> fib(int limit) {
long long a = 0, b = 1;
for (int i = 0; i < limit; ++i) {
co_yield a;
auto next = a + b; a = b; b = next;
}
}
int main() {
auto gen = fib(10);
while (gen.next()) std::cout << gen.value() << ", ";
// → 0, 1, 1, 2, 3, 5, 8, 13, 21, 34,
}
2.4 Modules 模块化(彻底告别 #include)
构建革命自 C 语言诞生 50 年来 C/C++ 一直依赖文本级 include + 预处理,1000 行 hello world 预处理后膨胀到几十万行。C++20 Modules 用编译后的二进制模块接口单元(BMI)替代文本拷贝,编译时间下降 5~10 倍,并且没有宏泄露、没有 ADL 跨 include 污染、符号可见性可控。语法三件套:export module M;(生产模块)、export template/decl...(对外导出)、import M;(使用模块)。
/* =============== 文件 A: mathlib.ixx (模块接口文件) =============== */
export module mathlib; // 声明本文件导出模块 mathlib
export namespace math { // namespace 里的符号一起导出(也可单独 export 某个函数/类)
// 对外公开的 gcd、lcm
constexpr long long gcd(long long a, long long b) {
while (b) { auto t = a % b; a = b; b = t; } return a;
}
constexpr long long lcm(long long a, long long b) { return a / gcd(a,b) * b; }
class Vec3 { // export namespace 包裹 → 类也一并导出
public:
double x{}, y{}, z{};
double dot(const Vec3& o) const { return x*o.x + y*o.y + z*o.z; }
};
// 模块内"不 export"的函数:只在本模块内部可见(类似 TU static 但强得多)
constexpr double internalHelper() { return 3.14159; }
/* =============== 文件 B: main.cpp (用户) =============== */
import mathlib; // 不再 #include 任何头文件!编译直接读取 BMI,编译飞快
#include <iostream> // ← 仍可以 import 和 include 混用(过渡期)
int main() {
// math 命名空间里的 API 全部可用
static_assert(math::gcd(48, 18) == 6);
math::Vec3 a{1,2,3}, b{4,5,6};
std::cout << a.dot(b) << "\n"; // 32
// internalHelper(); // ❌ 编译错误:未定义标识符(模块内私有)
}
三、核心语言其他关键改进
3.1 三路比较运算符 <=>(Spaceship 飞船)+ == 自动生成
代码量大减之前自定义类型完整比较语义要手写 operator < > <= >= == != 共 6 个函数,100+ 行重复代码。C++20 auto operator<=>(const T&) = default; 一行搞定全序/偏序,并顺带自动生成 ==/!=/</>/<=/>= 全部比较。
#include <compare>
#include <string>
#include <vector>
#include <algorithm>
struct Point {
int x, y;
// 一行搞定全部 6 个比较运算符(按成员逐个比较,与手写等价)
friend auto operator<=>(const Point&, const Point&) = default;
};
struct Student {
std::string name;
int grade;
double score;
// 自定义优先级:先按年级分,再按分数,最后按姓名
friend std::strong_ordering operator<=>(const Student& a, const Student& b) {
if (auto c = a.grade <=> b.grade; c != 0) return c;
if (auto c = b.score <=> a.score; c != 0) return c; // 分数降序:b vs a 对调
return a.name <=> b.name;
}
// ✅ 另外 operator== 会被编译器自动生成(不再需要再写)
};
int main() {
// 一行 default 带来的 6 个运算符全部立即可用:
Point a{1,2}, b{1,3};
auto r1 = (a < b); // true
auto r2 = (a == b); // false
auto r3 = (a <= b); // true …… 等等全部可用
// 更重要:自定义 Student 可以直接 sort,完全按照定义的 年级→分数→姓名 排序规则
std::vector<Student> ss = {{"Alice",3,95},{"Bob",2,88},{"Carol",3,90}};
std::sort(ss.begin(), ss.end());
}
3.2 consteval 立即函数 / constinit / requires 表达式
consteval:比 constexpr 更进一步——必须在编译期完成调用,任何尝试在运行期调用都直接编译错误(俗称"立即函数"),能做"我要求这个常量一定是编译期算出来"的强约束。constinit:修饰全局/静态变量,强制编译期初始化,消灭"静态初始化顺序 fiasco"。requires 表达式:返回 bool 的编译期表达式,用于 Concepts、constexpr if 中做"类型有没有某成员/操作"的检查。
#include <bit>
#include <string>
#include <cstring>
/* consteval:必须编译期算,运行期直接调用 → 编译错误 */
consteval size_t crc16(const char* s, size_t len) {
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < len; ++i) {
crc ^= (uint8_t)s[i];
for (int b = 0; b < 8; ++b) crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : (crc >> 1);
}
return crc;
}
/* constinit:强制全局变量编译期初始化,没有 SIOF 问题 */
constinit int g_bootCount = 0; // 非 const,但仍在编译期先赋值为 0
constinit const char* g_appName = "RoboKit";
int main() {
static_assert(crc16("HELLO", 5) == 0x7A5B); // ✅ 编译期 CRC 计算
// int n = 5; crc16("HELLO", n); // ❌ 编译错误:调用 consteval 需要常量表达式
/* requires 表达式:检查类型是否有某成员 / 可进行某操作 */
// T 能否支持 begin()/end()?→ bool 常量
template<typename T>
constexpr bool IsIterable =
requires(T& x) { x.begin(); x.end(); };
static_assert(IsIterable<std::string>);
static_assert(!IsIterable<int>);
}
3.3 [[likely]] / [[unlikely]] · char8_t · 指定初始化器
小升级[[likely/unlikely]] 提示编译器分支概率,做 if/switch 的代码布局/内联优化(类似 __builtin_expect 标准化)。char8_t 专门 UTF-8 字符类型,避免和 signed char 混用乱码。指定初始化器像 C99 一样写 Point{.x=10, .y=20},结构体配置参数再也不怕顺序错。
#include <string>
/* 1) [[likely]] / [[unlikely]]:提示分支热路径(替代 __builtin_expect)*/
int parseHeader(const char* buf, size_t n) {
if (n < 8) [[unlikely]] {
return -1; // 异常路径:编译器会移到冷区,热点不占 icache
}
[[likely]] if (buf[0] == 'M' && buf[1] == 'Z') {
/* PE 头部:热路径,编译器会围绕它做更好的分支预测/内联 */
return 0;
}
return -2;
}
/* 2) char8_t:专门的 UTF-8 代码单元类型,不再和普通 char/signed char 互转 */
const char8_t* utf8_s = u8"你好,世界! 🌍"; // u8 前缀字符串字面量现在类型是 const char8_t[]
/* 3) 指定初始化器 designated initializers:按名赋值,顺序随意 */
struct WindowConfig {
int width = 1280;
int height = 720;
bool fullscreen = false;
std::string title = "App";
float opacity = 1.0f;
};
int main() {
// ✅ 只想覆盖"全屏+标题"两个字段:不用把 5 个字段全写一遍
WindowConfig cfg = {
.fullscreen = true,
.title = "机器人示教器 - v2.3",
// 其余字段用声明处的默认值:width 1280, height 720, opacity 1.0
};
}
四、标准库:format / span / 原子引用 / source_location / erase_if
4.1 std::format 格式化库(printf / iostream 终结者)
效率+安全综合了 printf 的简洁占位符语法和 iostream 的类型安全,是 Rust/Python 的 format 家族标准化产物。支持位置参数、命名参数、对齐填充、宽度、精度、千分位、浮点数格式、布尔名输出、chrono 时间格式化……性能比 C 风格 sprintf 普遍快 2~3 倍。
#include <format>
#include <string>
#include <iostream>
#include <chrono>
using namespace std::literals;
int main() {
// (1) 默认位置参数 {},和 Python 完全一致
std::string s1 = std::format("用户[{}] 得分 {:.2f},排名第 {} 位", "Alice", 95.345, 3);
// → "用户[Alice] 得分 95.35,排名第 3 位"(浮点自动四舍五入到 2 位)
// (2) 编号参数:可重用 / 乱序
auto s2 = std::format("{1} × {0} × {1} = {2}", 6, 7, 6*7*7); // 7×6×7 = 294
// (3) 对齐 / 填充 / 宽度:{:[填充][对齐][宽度]}
// < 左对齐 ^ 居中 > 右对齐
auto s3 = std::format("|{:<10}| |{:*^10}| |{:>10,}|", "hello", 42, 1234567);
// → |hello | |****42****| | 1,234,567|
// ↑ 用 * 填充、居中、宽度 10 ↑ 逗号千分位
// (4) 布尔名、十六进制、八进制、二进制
auto s4 = std::format("{:s} / {:b} / {:x} / {:#x}", true, 42, 0xFF, 0xCAFE);
// → true / 101010 / ff / 0xcafe
// (5) 格式化 chrono 时间(C++20 真正能用)
auto today = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now());
auto s5 = std::format("今天是 {:%Y-%m-%d %A}", today); // e.g. "今天是 2026-08-10 Monday"
// (6) format_to:直接写到输出迭代器,避免临时 string 分配
std::string out;
std::format_to(std::back_inserter(out), "[LOG] {} pid={}", "boot", 1234);
std::cout << out << "\n";
}
4.2 std::span<T, Extent> 连续视图
零拷贝string_view 的泛化版——"指向连续 T 对象 + 长度"的胖指针。统一地吃 std::vector / std::array / T[N] 原生数组 / initializer_list / 子 span,接口几乎和 vector 一样,但 100% 零拷贝零分配。把函数参数 const std::vector<T>& 换 std::span<const T> 是 C++20 的最高效改法。
#include <span>
#include <vector>
#include <array>
#include <numeric>
#include <iostream>
// 函数不关心调用者是 vector / 数组 / array —— 统一写 span
double avg(std::span<const int> data) { // const 视图,只读 + 零拷贝
if (data.empty()) return 0;
double sum = std::accumulate(data.begin(), data.end(), 0.0);
return sum / data.size();
}
// 对数据就地缩放(读写 span)
void scale(std::span<int> data, int k) {
for (auto& x : data) x *= k;
}
int main() {
int carr[] = {1,2,3,4,5};
std::vector vec = {10,20,30,40};
std::array arr = {100,200};
// 所有类型都能直接传入 avg:无需构造临时 vector
avg(carr); // 3
avg(vec); // 25
avg(arr); // 150
// 子视图:subspan(first, count) / first(N) / last(N)
std::span all(carr); // 整个 {1,2,3,4,5}
auto head = all.first(3); // {1,2,3}
auto tail = all.subspan(2, 2); // 从 offset=2 取 2 个 → {3,4}
// 写 span 直接改原数组
scale(head, 10); // 改完 carr → {10,20,30,4,5}
}
4.3 source_location / erase_if / atomic_ref
source_location:标准化 __FILE__/__LINE__ 宏,可做默认参数,不用宏即可写 Log(msg, file, line, func) 的自动定位日志函数。
erase_if:一行从 map/set/vector 按谓词擦除,替代 erase-remove 惯用法。atomic_ref:对已存在的普通对象做原子读写,不用强制换类型。
#include <source_location>
#include <string_view>
#include <iostream>
#include <vector>
#include <map>
#include <atomic>
/* 1) source_location:默认参数即可获得调用点的文件/行号 —— 不需要再写宏了! */
enum class Level { DBG, INF, WRN, ERR };
void log(Level lv, std::string_view msg,
const std::source_location& loc = std::source_location::current()) {
const char* lvs = "DIWE";
std::cout << std::format("[{}] {}:{} {} - {}\n",
lvs[(int)lv], loc.file_name(), loc.line(), loc.function_name(), msg);
}
void doWork() {
log(Level::INF, "开始处理任务"); // 自动:file=xx.cpp line=此行 function=doWork
/* ... */
log(Level::ERR, "超时:连接断开");
}
/* 2) std::erase_if:一行按条件擦除,替代 erase-remove_if 两句 */
int main() {
std::vector v = {1,2,3,4,5,6,7,8};
std::erase_if(v, [](int x){ return x % 2 == 0; }); // 只留奇数 {1,3,5,7}
std::map<int,std::string> mp = {{1,"a"},{2,"b"},{3,"c"}};
std::erase_if(mp, [](auto& kv){ return kv.first % 2 == 0; }); // 删掉偶数键,map 也能 erase_if!
/* 3) atomic_ref:把一个普通 int 临时变成"原子读写"(不改变类型)*/
int counter = 0; // 类型还是 int,嵌入到 POD/共享内存/寄存器映射都没问题
{
std::atomic_ref<int> atom(counter);
atom.fetch_add(100, std::memory_order_relaxed); // 原子自增
}
// counter == 100
}
五、并发新基建 & 日历时区
5.1 std::jthread + std::stop_token
并发安全C++11 std::thread 的一大坑:析构时如果还 joinable,直接 std::terminate 崩溃。jthread(joinable thread)= RAII 线程:析构自动 request_stop + join,绝不崩溃。配套的 stop_token / stop_source / stop_callback 是标准化的协作式停止令牌,condition_variable_any 也支持等待中直接被 stop 打断,再也不用手写 volatile bool m_stop 了。
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <iostream>
using namespace std::literals;
/* 用法 1:jthread 析构自动 join;stop_token 用于定期轮询退出 */
void workerLoop(std::stop_token st) {
// 循环条件:未被外部请求停止
while (!st.stop_requested()) {
std::this_thread::sleep_for(200ms);
std::cout << "[worker] 心跳…\n";
}
std::cout << "[worker] 停止令牌已到,安全退出\n";
}
/* 用法 2:condition_variable_any + stop_token — wait 可被 stop 直接打断,不用虚假唤醒+轮询 */
void consumer(std::stop_token st, std::mutex& m, std::condition_variable_any& cv,
std::queue<int>& q) {
std::unique_lock lk(m);
while (true) {
cv.wait(lk, st, [&]{ return !q.empty(); }); // 等条件 OR 被 stop,二选一即唤醒
if (st.stop_requested()) break; // ✅ stop_token 已触发 → 干净退出
int v = q.front(); q.pop();
lk.unlock();
// 处理 v ...
lk.lock();
}
}
int main() {
// 启动 2 个 jthread:离开作用域时会自动 request_stop + join(RAII)
std::jthread t1(workerLoop);
std::jthread t2(workerLoop);
std::this_thread::sleep_for(1s);
// t.request_stop() 可手动触发;即使不写,jthread 析构也会自动触发 + join,绝不 terminate
}
5.2 chrono 日历与时区:真正可用的日期库
工程化C++11 的 chrono 只提供"时间点 / 时长 / 时钟"三件套,年月日星期几全得自己算。C++20 把 Howard Hinnant 的 date 库标准化:year / month / day / weekday / year_month_day / zoned_time / tzdb 等 30+ 个类型全部到位。直接支持:公历日期运算、当天星期几、本月第几个星期几、IANA 时区数据库、夏令时转换、Unix 时间戳互转、格式化输出。
#include <chrono>
#include <format>
#include <iostream>
namespace chr = std::chrono;
int main() {
/* 1) 构建一个年/月/日日期字面量(C++20 全面支持 + 运算符 / 字面量)*/
using namespace chr::literals;
auto birth = 1995y / chr::June / 15d; // 1995-06-15(year_month_day 类型,POD)
// 也能写 day/month/year:15d / 6 / 1995y —— 三种顺序都 OK
/* 2) 字段取值 + 星期几 + 校验合法 */
auto wd = chr::weekday{chr::sys_days{birth}}; // 转 sys_days 再取 weekday
bool valid = birth.ok(); // 是否合法日期(避免 2024/2/30)
std::cout << std::format("出生日期 {:%Y-%m-%d (%A)},valid={}\n", birth, wd, valid);
/* 3) 日期算术:30 天后 / 6 个月前 / 今年的第几周 */
chr::year_month_day due = chr::sys_days{birth} + chr::days{30};
chr::year_month_day past = chr::sys_days{chr::local_days{birth}} - chr::months{6};
/* 4) 时区转换(IANA TZDB):UTC → 上海时区 */
auto nowUTC = chr::system_clock::now();
chr::zoned_time nowShanghai{"Asia/Shanghai", nowUTC};
chr::zoned_time nowNY{"America/New_York", nowUTC};
std::cout << std::format("上海时间: {:%Y-%m-%d %H:%M:%S %Z}\n", nowShanghai);
std::cout << std::format("纽约时间: {:%Y-%m-%d %H:%M:%S %Z}\n", nowNY);
/* 5) Unix 时间戳互转 */
auto ts = chr::duration_cast<chr::seconds>(nowUTC.time_since_epoch()).count();
auto fromTs = chr::sys_days{chr::seconds{ts}};
}
六、C++20 特性总览对照表
| 分类 | 特性 | 一句话价值 |
|---|---|---|
| 四大天王 | Concepts + requires | 模板约束可读、1 行报错替代 50 页 SFINAE 堆栈 |
| Ranges + Views + 管道 | | 惰性算法流水线,零中间容器 / 声明式数据处理 | |
| Coroutines | 语言级协程:异步 IO / 生成器 / 事件驱动轻松写 | |
| Modules 模块化 | 告别 #include / 宏污染,编译时间 5~10 倍加速 | |
| 核心语言 | <=> 飞船运算符 | 一行 default 生成 6 个比较运算符 |
| consteval / constinit | 强制编译期执行 / 静态初始化顺序 fiasco 根治 | |
| [[likely/unlikely]] / char8_t / 指定初始化 | 提示分支预测 / UTF-8 专类型 / C99 指定字段赋值 | |
| 标准库 | std::format | printf+iostream 的最佳接班人,类型安全 + 高性能 |
| std::span | 连续数组零拷贝视图,消灭 vector 参数临时分配 | |
| source_location / erase_if / atomic_ref | 日志不用宏;一行按条件擦除容器;普通对象原子化 | |
| bit_cast / midpoint / lerp / starts_with / ends_with | 常用运算 & 字符串小工具终于标准化 | |
| 并发 + 时间 | jthread / stop_token | RAII 线程 + 协作停止令牌,永不 terminate |
| Calendar + Timezone chrono | 年月日 / 星期 / 时区 IANA 数据库 / 格式化 |
总结 & 升级建议
- 最快立竿见影:format + spans + erase_if + source_location:几乎零学习成本,代码量和性能立刻改善。
- 新项目默认使用 Concepts 约束模板参数:SFINAE 时代已经过去。用 Concepts 写的库,使用者报错是"人话",用户会感谢你。
- 数据处理全面切换到 Ranges Views 管道:再也不写多层嵌套 STL 算法 + 手写中间 vector 循环。链式管道可读性、性能双优。
- 并发重构:jthread/stop_token 全面替换 std::thread + 手写 stop flag;condition_variable_any 替代老 cv 以支持中断等待。
- 长期布局:模块化改造。对任何超过 50 个编译单元的工程,Modules 替换头文件 include 往往带来超过 50% 的编译加速,并彻底解决"宏污染 / ODR 冲突"两大历史问题。