← 返回博客列表

一、概述

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 的体验已经差别不大。

二、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::formatprintf+iostream 的最佳接班人,类型安全 + 高性能
std::span连续数组零拷贝视图,消灭 vector 参数临时分配
source_location / erase_if / atomic_ref日志不用宏;一行按条件擦除容器;普通对象原子化
bit_cast / midpoint / lerp / starts_with / ends_with常用运算 & 字符串小工具终于标准化
并发 + 时间jthread / stop_tokenRAII 线程 + 协作停止令牌,永不 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 冲突"两大历史问题。