C++20 新特性全面总结
核心思想:C++20 是继 C++11 之后最大的一次语言变革,被称为"C++的第二次革命"。它引入了四大旗舰特性------Concepts(概念) 、Ranges(范围) 、Coroutines(协程) 、Modules(模块) ,同时在语言和标准库层面带来了三路比较运算符、constexpr 的全面扩展、std::format、std::span、日历/时区、协作式线程取消等大量现代化基础设施,标志着 C++ 正式进入"声明式、安全、高表达力"的新时代。
🚀 1. Concepts(概念)------ 约束模板参数
1.1 问题与动机:
C++
/*
* C++17 模板错误信息的痛苦:
* ┌────────────────────────────────────────────────────────────┐
* │ template <typename T> │
* │ void sort(T& container); │
* │ │
* │ sort(42); // 编译错误 → 几十行不可读的错误信息 │
* │ "no matching function for call to 'sort'..." │
* │ "...in instantiation of template..." │
* │ "...std::__1::__wrap_iter<int*>..." (天书) │
* └────────────────────────────────────────────────────────────┘
*
* C++20 Concepts 的目标:
* ┌────────────────────────────────────────────────────────────┐
* │ 1. 让模板参数的约束显式化、可读化 │
* │ 2. 提供清晰的编译错误信息 │
* │ 3. 替代 SFINAE / enable_if 的复杂手段 │
* │ 4. 支持基于约束的重载决议 │
* └────────────────────────────────────────────────────────────┘
*/
1.2 定义与使用 Concept:
C++
#include <concepts>
#include <type_traits>
// ═══ 定义 concept ═══
template <typename T>
concept Numeric = std::is_arithmetic_v<T>;
template <typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::convertible_to<T>; // a+b 的结果可转为 T
};
template <typename T>
concept Printable = requires(std::ostream& os, T val) {
{ os << val } -> std::same_as<std::ostream&>;
};
template <typename T>
concept Hashable = requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
// 组合 concept
template <typename T>
concept PrintableNumeric = Numeric<T> && Printable<T>;
// ═══ 使用 concept 的四种语法 ═══
// 语法1:requires 子句
template <typename T>
requires Numeric<T>
T add(T a, T b) { return a + b; }
// 语法2:尾置 requires
template <typename T>
T multiply(T a, T b) requires Numeric<T> { return a * b; }
// 语法3:受约束的模板参数(最推荐)
template <Numeric T>
T divide(T a, T b) { return a / b; }
// 语法4:简写函数模板(最简洁)
auto square(Numeric auto x) { return x * x; }
void concept_usage() {
add(1, 2); // ✅ int 满足 Numeric
add(1.5, 2.5); // ✅ double 满足 Numeric
// add("a", "b"); // ❌ 清晰的错误:"const char*" does not satisfy Numeric
square(42); // ✅
// square("hello"); // ❌ 清晰报错
}
1.3 requires 表达式详解:
C++
// requires 表达式的四种要求
template <typename T>
concept Container = requires(T c, typename T::value_type v) {
// 1. 简单要求:表达式必须合法
c.begin();
c.end();
c.size();
// 2. 类型要求:类型必须存在
typename T::value_type;
typename T::iterator;
typename T::size_type;
// 3. 复合要求:表达式合法 + 返回类型约束 + 是否 noexcept
{ c.size() } noexcept -> std::convertible_to<std::size_t>;
{ c.begin() } -> std::input_or_output_iterator;
{ *c.begin() } -> std::same_as<typename T::value_type&>;
// 4. 嵌套要求:嵌套一个 requires 判断布尔条件
requires std::default_initializable<T>;
requires (sizeof(T) >= 8);
};
// 实际应用:约束排序算法
template <typename T>
concept Sortable = requires(T a, T b) {
{ a < b } -> std::convertible_to<bool>;
{ a = std::move(b) };
};
template <std::random_access_iterator Iter>
requires Sortable<std::iter_value_t<Iter>>
void my_sort(Iter begin, Iter end) {
std::sort(begin, end);
}
1.4 标准库预定义 Concepts:
C++
#include <concepts>
#include <iterator>
#include <ranges>
/*
* <concepts> 中的核心概念:
* ┌──────────────────────────┬─────────────────────────────────┐
* │ 语言概念 │ 说明 │
* ├──────────────────────────┼─────────────────────────────────┤
* │ std::same_as<T, U> │ T 和 U 是同一类型 │
* │ std::derived_from<D, B> │ D 继承自 B │
* │ std::convertible_to<F,T> │ F 可隐式转为 T │
* │ std::integral<T> │ T 是整数类型 │
* │ std::floating_point<T> │ T 是浮点类型 │
* │ std::default_initializable│ T 可默认构造 │
* │ std::copy_constructible │ T 可拷贝构造 │
* │ std::move_constructible │ T 可移动构造 │
* │ std::assignable_from │ 可赋值 │
* │ std::destructible │ 可析构 │
* ├──────────────────────────┼─────────────────────────────────┤
* │ 比较概念 │ │
* ├──────────────────────────┼─────────────────────────────────┤
* │ std::equality_comparable │ 支持 == 和 != │
* │ std::totally_ordered │ 支持 <, >, <=, >= │
* ├──────────────────────────┼─────────────────────────────────┤
* │ 可调用概念 │ │
* ├──────────────────────────┼─────────────────────────────────┤
* │ std::invocable<F, Args> │ F 可用 Args 调用 │
* │ std::predicate<F, Args> │ F 是返回 bool 的谓词 │
* │ std::relation<R, T, U> │ R 是 T,U 上的二元关系 │
* └──────────────────────────┴─────────────────────────────────┘
*/
// 使用标准概念
template <std::integral T>
T gcd(T a, T b) {
while (b != 0) { a %= b; std::swap(a, b); }
return a;
}
void process(std::invocable<int> auto&& fn) {
fn(42);
}
// 基于约束的重载决议
template <std::integral T>
std::string to_str(T val) { return "int:" + std::to_string(val); }
template <std::floating_point T>
std::string to_str(T val) { return "float:" + std::to_string(val); }
void overload_demo() {
std::cout << to_str(42) << "\n"; // "int:42"
std::cout << to_str(3.14) << "\n"; // "float:3.140000"
}
1.5 Concept 子包含(Subsumption):
C++
// 更受约束的 concept 优先匹配
template <typename T>
concept Animal = requires(T a) { a.breathe(); };
template <typename T>
concept Dog = Animal<T> && requires(T d) { d.bark(); };
// Dog 子包含(subsumes)Animal → Dog 更受约束
void process(Animal auto a) { std::cout << "Animal\n"; }
void process(Dog auto d) { std::cout << "Dog\n"; }
struct Labrador {
void breathe() {}
void bark() {}
};
struct Fish {
void breathe() {}
};
void subsumption_demo() {
process(Labrador{}); // "Dog" ← 选择更受约束的重载
process(Fish{}); // "Animal"
}
🌊 2. Ranges(范围库)
2.1 核心理念:
C++
/*
* Ranges 的核心改进:
* ┌──────────────────────────────────────────────────────────────┐
* │ 1. 算法直接接受容器(不再需要 begin/end 对) │
* │ 2. 视图(Views)实现惰性求值和管道组合 │
* │ 3. 投影(Projections)替代繁琐的 Lambda │
* │ 4. 概念约束提供清晰的编译错误 │
* └──────────────────────────────────────────────────────────────┘
*/
#include <ranges>
#include <algorithm>
#include <vector>
void ranges_vs_classic() {
std::vector<int> v = {5, 3, 1, 4, 2};
// ❌ C++17 经典写法
std::sort(v.begin(), v.end());
auto it = std::find(v.begin(), v.end(), 3);
// ✅ C++20 Ranges 写法
std::ranges::sort(v);
auto it2 = std::ranges::find(v, 3);
// 投影(Projection)
struct Person { std::string name; int age; };
std::vector<Person> people = {{"Bob", 25}, {"Alice", 30}, {"Charlie", 20}};
// ❌ C++17:需要写 Lambda
std::sort(people.begin(), people.end(),
[](const Person& a, const Person& b) { return a.age < b.age; });
// ✅ C++20:使用投影
std::ranges::sort(people, {}, &Person::age); // 按 age 升序
std::ranges::sort(people, std::greater{}, &Person::age); // 按 age 降序
auto youngest = std::ranges::min(people, {}, &Person::age);
}
2.2 视图(Views)与管道运算符:
C++
#include <ranges>
#include <vector>
#include <iostream>
namespace views = std::views; // 别名
void views_example() {
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 管道语法:过滤偶数 → 平方 → 取前3个
auto result = nums
| views::filter([](int n) { return n % 2 == 0; }) // {2,4,6,8,10}
| views::transform([](int n) { return n * n; }) // {4,16,36,64,100}
| views::take(3); // {4,16,36}
for (int v : result) {
std::cout << v << " "; // 4 16 36
}
std::cout << "\n";
// ✅ 惰性求值!上面的链不会立即计算,遍历时才逐个处理
// ✅ 零内存分配!视图不拷贝数据,只是"观察"原数据
}
// 常用视图适配器
void common_views() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// filter:过滤
auto evens = v | views::filter([](int n) { return n % 2 == 0; });
// transform:变换
auto doubled = v | views::transform([](int n) { return n * 2; });
// take / drop:取前N个 / 跳过前N个
auto first3 = v | views::take(3); // {1, 2, 3}
auto skip3 = v | views::drop(3); // {4, 5, 6, 7, 8, 9, 10}
// take_while / drop_while:条件截取
auto until5 = v | views::take_while([](int n) { return n < 5; }); // {1,2,3,4}
// reverse:反转
auto rev = v | views::reverse; // {10, 9, ..., 1}
// keys / values:提取 pair/tuple 的 key/value
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
auto keys = m | views::keys; // {"a", "b"}
auto vals = m | views::values; // {1, 2}
// elements<N>:提取 tuple 的第 N 个元素
std::vector<std::tuple<int, char, double>> tuples = {{1,'a',1.1}, {2,'b',2.2}};
auto chars = tuples | views::elements<1>; // {'a', 'b'}
// iota:生成序列
auto numbers = views::iota(1, 11); // {1, 2, ..., 10}
auto infinite = views::iota(1); // {1, 2, 3, ...} 无穷序列
// 复合管道
for (int x : views::iota(1)
| views::filter([](int n) { return n % 3 == 0; })
| views::transform([](int n) { return n * n; })
| views::take(5)) {
std::cout << x << " "; // 9 36 81 144 225
}
// split / join(字符串处理)
std::string csv = "hello,world,foo,bar";
for (auto word : csv | views::split(',')) {
std::cout << std::string_view(word) << "\n";
}
// zip(C++23,但这里提一下设计思路)
// auto zipped = views::zip(v1, v2); // C++23
}
2.3 自定义 Range 与实际应用:
C++
// 实际应用:数据处理管道
struct Employee {
std::string name;
std::string department;
double salary;
};
void data_pipeline() {
std::vector<Employee> employees = {
{"Alice", "Engineering", 120000},
{"Bob", "Marketing", 85000},
{"Charlie", "Engineering", 95000},
{"Diana", "Engineering", 110000},
{"Eve", "Marketing", 92000}
};
// 找出工程部门薪资前2高的员工名
auto top_eng_names = employees
| views::filter([](const Employee& e) {
return e.department == "Engineering";
})
| views::transform(&Employee::name)
| views::take(2); // 注意:未排序,需先排序才有意义
// 先排序再管道
std::ranges::sort(employees, std::greater{}, &Employee::salary);
for (const auto& name : employees
| views::filter([](const Employee& e) {
return e.department == "Engineering";
})
| views::transform(&Employee::name)
| views::take(2)) {
std::cout << name << "\n"; // Alice, Diana
}
}
🔄 3. Coroutines(协程)
3.1 协程基础概念:
C++
/*
* 协程 vs 普通函数:
* ┌───────────────┬────────────────────────────────────────────────┐
* │ 普通函数 │ 调用 → 执行完毕 → 返回(一气呵成) │
* │ 协程 │ 调用 → 暂停 → 恢复 → 暂停 → ... → 完成 │
* │ │ (可以在中途挂起,保留状态,稍后恢复) │
* ├───────────────┼────────────────────────────────────────────────┤
* │ 关键字 │ co_await:挂起,等待异步结果 │
* │ │ co_yield:挂起,产生一个值(生成器) │
* │ │ co_return:最终返回 │
* ├───────────────┼────────────────────────────────────────────────┤
* │ 应用场景 │ 异步 I/O、生成器、状态机、事件驱动 │
* └───────────────┴────────────────────────────────────────────────┘
*
* ⚠️ C++20 只提供了协程的底层机制(编译器支持),
* 没有提供开箱即用的协程类型。
* 实际使用通常借助第三方库(cppcoro)或自行实现 Promise/Awaitable。
* C++23 引入了 std::generator。
*/
3.2 生成器(Generator)示例:
C++
#include <coroutine>
#include <iostream>
#include <optional>
// 最简生成器实现
template <typename T>
class Generator {
public:
struct promise_type {
T current_value;
Generator get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(T value) {
current_value = std::move(value);
return {};
}
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
// 迭代器支持(简化版)
struct iterator {
std::coroutine_handle<promise_type> handle_;
bool done_;
iterator& operator++() {
handle_.resume();
done_ = handle_.done();
return *this;
}
T& operator*() { return handle_.promise().current_value; }
bool operator!=(std::default_sentinel_t) const { return !done_; }
};
iterator begin() {
handle_.resume();
return {handle_, handle_.done()};
}
std::default_sentinel_t end() { return {}; }
explicit Generator(std::coroutine_handle<promise_type> h) : handle_(h) {}
~Generator() { if (handle_) handle_.destroy(); }
Generator(const Generator&) = delete;
Generator(Generator&& other) noexcept : handle_(std::exchange(other.handle_, {})) {}
private:
std::coroutine_handle<promise_type> handle_;
};
// ✅ 使用协程定义无穷序列
Generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a; // 挂起,产出当前值
auto next = a + b;
a = b;
b = next;
}
}
Generator<int> range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i;
}
}
void coroutine_demo() {
// 惰性生成斐波那契数列
int count = 0;
for (int val : fibonacci()) {
std::cout << val << " ";
if (++count >= 10) break;
}
// 0 1 1 2 3 5 8 13 21 34
std::cout << "\n";
for (int val : range(1, 6)) {
std::cout << val << " "; // 1 2 3 4 5
}
}
3.3 异步协程(Async/Await 模式):
C++
// 概念性示例(需要自定义 Task 类型或使用库)
/*
Task<std::string> fetch_data(std::string url) {
auto response = co_await async_http_get(url); // 挂起等待网络
auto parsed = co_await async_parse(response); // 挂起等待解析
co_return parsed.body;
}
Task<void> process() {
auto data1 = co_await fetch_data("https://api.example.com/a");
auto data2 = co_await fetch_data("https://api.example.com/b");
std::cout << data1 << ", " << data2 << "\n";
}
*/
/*
* 协程底层机制:
* ┌─────────────────┬─────────────────────────────────────────┐
* │ coroutine_handle│ 协程句柄,用于恢复/销毁协程 │
* │ promise_type │ 协程的"承诺对象",控制行为 │
* │ co_await │ 等待一个 Awaitable 对象 │
* │ co_yield │ 产出值并挂起 │
* │ co_return │ 最终返回值 │
* │ Awaitable │ 需实现 await_ready/suspend/resume │
* └─────────────────┴─────────────────────────────────────────┘
*/
📦 4. Modules(模块)
C++
/*
* 模块 vs 头文件:
* ┌───────────────┬──────────────────────┬────────────────────┐
* │ │ #include 头文件 │ import 模块 │
* ├───────────────┼──────────────────────┼────────────────────┤
* │ 编译速度 │ 慢(重复解析) │ ✅ 快(预编译) │
* │ 宏泄漏 │ ❌ 宏跨文件污染 │ ✅ 宏不泄漏 │
* │ 符号可见性 │ 全部可见 │ ✅ 显式 export │
* │ include 顺序 │ ❌ 可能影响编译 │ ✅ 顺序无关 │
* │ ODR 违规 │ 容易出错 │ ✅ 大幅减少 │
* │ 编译器支持 │ 完善 │ ⚠️ 仍在完善中 │
* └───────────────┴──────────────────────┴────────────────────┘
*/
// ═══ 模块接口文件:math.cppm ═══
/*
export module math; // 声明模块名
// export 的函数/类/变量对外可见
export int add(int a, int b) {
return a + b;
}
export class Calculator {
public:
int multiply(int a, int b) { return a * b; }
};
// 未 export 的是模块内部实现
int internal_helper() { return 42; } // 外部不可见
*/
// ═══ 使用模块:main.cpp ═══
/*
import math; // 导入模块
import <iostream>; // 导入标准头文件作为模块
int main() {
std::cout << add(1, 2) << "\n";
Calculator calc;
std::cout << calc.multiply(3, 4) << "\n";
// internal_helper(); // ❌ 错误:未 export
}
*/
// ═══ 模块分区(Module Partitions)═══
/*
// math-impl.cppm
export module math:impl; // 模块分区
export int add(int a, int b) { return a + b; }
// math.cppm
export module math;
export import :impl; // 重新导出分区
*/
/*
* ⚠️ 模块的现状(截至2024):
* - MSVC 支持最好
* - GCC/Clang 支持逐步完善
* - CMake 3.28+ 开始支持 C++ 模块
* - 大型项目迁移仍需时间
*/
⚖️ 5. 三路比较运算符(<=>,Spaceship Operator)
C++
#include <compare>
/*
* <=> 运算符返回三种比较类别之一:
* ┌──────────────────────┬────────────────────────────────────┐
* │ std::strong_ordering │ 完全有序(相等的对象不可区分) │
* │ │ 如:int, string │
* ├──────────────────────┼────────────────────────────────────┤
* │ std::weak_ordering │ 弱排序(等价但可区分) │
* │ │ 如:大小写不敏感的字符串比较 │
* ├──────────────────────┼────────────────────────────────────┤
* │ std::partial_ordering│ 偏序(某些值不可比较) │
* │ │ 如:float(NaN 不可比较) │
* └──────────────────────┴────────────────────────────────────┘
*/
// ❌ C++17:手动实现6个比较运算符
struct Point_old {
int x, y;
bool operator==(const Point_old& o) const { return x == o.x && y == o.y; }
bool operator!=(const Point_old& o) const { return !(*this == o); }
bool operator< (const Point_old& o) const { return std::tie(x,y) < std::tie(o.x,o.y); }
bool operator<=(const Point_old& o) const { return !(o < *this); }
bool operator> (const Point_old& o) const { return o < *this; }
bool operator>=(const Point_old& o) const { return !(*this < o); }
};
// ✅ C++20:一行搞定所有比较
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // 生成全部6个运算符!
};
void spaceship_basic() {
Point a{1, 2}, b{1, 3}, c{1, 2};
std::cout << (a < b) << "\n"; // true
std::cout << (a == c) << "\n"; // true
std::cout << (a >= b) << "\n"; // false
// 直接使用 <=> 的结果
auto cmp = a <=> b;
if (cmp < 0) std::cout << "a < b\n";
else if (cmp > 0) std::cout << "a > b\n";
else std::cout << "a == b\n";
}
// 自定义比较逻辑
class Version {
int major_, minor_, patch_;
public:
Version(int ma, int mi, int p) : major_(ma), minor_(mi), patch_(p) {}
// 自定义 <=>(按 major → minor → patch 比较)
std::strong_ordering operator<=>(const Version& other) const {
if (auto cmp = major_ <=> other.major_; cmp != 0) return cmp;
if (auto cmp = minor_ <=> other.minor_; cmp != 0) return cmp;
return patch_ <=> other.patch_;
}
// ⚠️ 自定义 <=> 时,== 不会自动生成,需要显式声明
bool operator==(const Version&) const = default;
};
void version_demo() {
Version v1{2, 0, 0}, v2{1, 9, 9}, v3{2, 0, 0};
std::cout << (v1 > v2) << "\n"; // true
std::cout << (v1 == v3) << "\n"; // true
// 可以直接用于排序
std::vector<Version> versions = {{1,0,0}, {2,1,0}, {1,5,3}, {2,0,1}};
std::ranges::sort(versions);
}
// 不同类型之间的比较
class Celsius {
double temp_;
public:
explicit Celsius(double t) : temp_(t) {}
double value() const { return temp_; }
std::partial_ordering operator<=>(const Celsius& other) const {
return temp_ <=> other.temp_; // double 的 <=> 返回 partial_ordering
}
bool operator==(const Celsius&) const = default;
};
🔧 6. constexpr 的全面扩展
6.1 constexpr 新能力:
C++
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
/*
* C++20 constexpr 新增能力:
* ┌────────────────────────────┬──────┬──────┬──────┬──────┐
* │ 特性 │ C++11│ C++14│ C++17│ C++20│
* ├────────────────────────────┼──────┼──────┼──────┼──────┤
* │ 局部变量/循环/分支 │ ❌ │ ✅ │ ✅ │ ✅ │
* │ virtual 函数 │ ❌ │ ❌ │ ❌ │ ✅ │
* │ try-catch │ ❌ │ ❌ │ ❌ │ ✅ │
* │ dynamic_cast / typeid │ ❌ │ ❌ │ ❌ │ ✅ │
* │ new / delete(瞬态分配) │ ❌ │ ❌ │ ❌ │ ✅ │
* │ std::vector / std::string │ ❌ │ ❌ │ ❌ │ ✅ │
* │ 算法(sort/find等) │ ❌ │ ❌ │ ❌ │ ✅ │
* └────────────────────────────┴──────┴──────┴──────┴──────┘
*/
// ✅ constexpr 动态内存分配(瞬态 ------ 必须在编译期释放)
constexpr int sum_of_sorted() {
std::vector<int> v = {5, 3, 1, 4, 2}; // ✅ 编译期 vector!
std::sort(v.begin(), v.end()); // ✅ 编译期排序!
return std::accumulate(v.begin(), v.end(), 0);
// v 在编译期创建和销毁(瞬态分配)
}
static_assert(sum_of_sorted() == 15);
// ✅ constexpr std::string
constexpr auto make_greeting() {
std::string s = "Hello";
s += ", ";
s += "World!";
return s.size(); // 返回 size_t(非 string 本身,因为 string 不能跨越编译期/运行期边界)
}
static_assert(make_greeting() == 13);
// ✅ constexpr virtual
struct Base {
constexpr virtual int value() const { return 1; }
constexpr virtual ~Base() = default;
};
struct Derived : Base {
constexpr int value() const override { return 2; }
};
constexpr int get_value(const Base& b) { return b.value(); }
static_assert(get_value(Derived{}) == 2);
6.2 consteval ------ 强制编译期求值:
C++
// consteval:函数必须在编译期求值,不能在运行期调用
consteval int compile_time_square(int x) {
return x * x;
}
void consteval_example() {
constexpr int a = compile_time_square(5); // ✅ 编译期
// int x = 5;
// int b = compile_time_square(x); // ❌ 错误:x 不是编译期常量
const int c = compile_time_square(10); // ✅ 编译期(const + 常量表达式)
}
// 实际应用:编译期校验
consteval int safe_divide(int a, int b) {
if (b == 0) throw "Division by zero!"; // 编译期抛出 → 编译错误
return a / b;
}
constexpr int r1 = safe_divide(10, 2); // ✅ OK = 5
// constexpr int r2 = safe_divide(10, 0); // ❌ 编译错误!
// consteval 用于生成只在编译期存在的数据
consteval auto create_lookup_table() {
std::array<int, 256> table{};
for (int i = 0; i < 256; ++i) {
table[i] = (i * i) % 256;
}
return table;
}
constexpr auto lookup = create_lookup_table(); // 编译期生成查找表
6.3 constinit ------ 保证静态初始化:
C++
// constinit:保证变量在编译期初始化,但不要求变量是 const
// 解决"静态初始化顺序惨败"(Static Initialization Order Fiasco)
constinit int global_count = 0; // ✅ 编译期初始化为 0
// constinit int bad = rand(); // ❌ 错误:rand() 不是常量表达式
constinit thread_local int tls_val = 42; // ✅ 线程局部变量也可以
void constinit_example() {
global_count = 100; // ✅ 运行期可以修改(不是 const)
// 与 constexpr 的区别:
// constexpr → 编译期初始化 + 不可修改
// constinit → 编译期初始化 + 可以修改
}
/*
* 三者对比:
* ┌──────────┬──────────────────┬───────────────┬───────────────┐
* │ │ 编译期初始化 │ 编译期可用 │ 运行期可修改 │
* ├──────────┼──────────────────┼───────────────┼───────────────┤
* │ const │ 不保证 │ 不保证 │ ❌ │
* │ constexpr│ ✅ 保证 │ ✅ │ ❌ │
* │ constinit│ ✅ 保证 │ ❌ │ ✅ │
* │ consteval│ ---(用于函数) │ ✅ 强制 │ --- │
* └──────────┴──────────────────┴───────────────┴───────────────┘
*/
📝 7. std::format ------ 现代格式化
C++
#include <format>
#include <string>
#include <iostream>
/*
* std::format vs printf vs iostream:
* ┌────────────┬───────────┬───────────┬────────────────┐
* │ │ printf │ iostream │ std::format │
* ├────────────┼───────────┼───────────┼────────────────┤
* │ 类型安全 │ ❌ │ ✅ │ ✅ │
* │ 可扩展 │ ❌ │ ✅ (<<) │ ✅ (formatter) │
* │ 性能 │ ✅ 快 │ ❌ 慢 │ ✅ 快 │
* │ 可读性 │ ⚠️ 一般 │ ❌ 链式差 │ ✅ 最好 │
* │ 本地化 │ ⚠️ │ ✅ │ ✅ │
* └────────────┴───────────┴───────────┴────────────────┘
*/
void format_basic() {
// 基本用法
std::string s = std::format("Hello, {}!", "World");
std::cout << s << "\n"; // "Hello, World!"
// 多个参数
std::string info = std::format("{} is {} years old", "Alice", 30);
// 位置参数
std::string ordered = std::format("{1} before {0}", "World", "Hello");
// "Hello before World"
// 格式规范
std::cout << std::format("{:>10}", "right") << "\n"; // " right"(右对齐)
std::cout << std::format("{:<10}", "left") << "\n"; // "left "(左对齐)
std::cout << std::format("{:^10}", "center") << "\n"; // " center "(居中)
std::cout << std::format("{:*^10}", "hi") << "\n"; // "****hi****"(填充字符)
// 数字格式
std::cout << std::format("{:d}", 42) << "\n"; // "42"(十进制)
std::cout << std::format("{:x}", 255) << "\n"; // "ff"(十六进制)
std::cout << std::format("{:#x}", 255) << "\n"; // "0xff"
std::cout << std::format("{:b}", 42) << "\n"; // "101010"(二进制)
std::cout << std::format("{:#010b}", 42) << "\n"; // "0b00101010"
std::cout << std::format("{:o}", 42) << "\n"; // "52"(八进制)
// 浮点格式
std::cout << std::format("{:.2f}", 3.14159) << "\n"; // "3.14"
std::cout << std::format("{:e}", 12345.6) << "\n"; // "1.234560e+04"
std::cout << std::format("{:10.3f}", 3.14) << "\n"; // " 3.140"
// 直接输出(C++23 有 std::print,C++20 用 format + cout)
std::cout << std::format("x={}, y={}, z={}\n", 1, 2.5, "three");
}
// 自定义类型的格式化支持
struct Point {
double x, y;
};
template <>
struct std::formatter<Point> {
constexpr auto parse(std::format_parse_context& ctx) {
return ctx.begin();
}
auto format(const Point& p, std::format_context& ctx) const {
return std::format_to(ctx.out(), "({:.2f}, {:.2f})", p.x, p.y);
}
};
void custom_format() {
Point p{1.5, 2.7};
std::cout << std::format("Point: {}\n", p); // "Point: (1.50, 2.70)"
}
🔍 8. std::span ------ 连续内存的非拥有视图
C++
#include <span>
#include <vector>
#include <array>
/*
* std::span<T>:对连续内存的非拥有、轻量级视图
* ┌────────────────┬──────────────────────────────────────────────┐
* │ 类比 │ string_view 之于 string,span 之于 vector │
* │ 大小 │ 指针 + 长度(16字节) │
* │ 可以指向 │ C数组、vector、array、new[] 等连续内存 │
* │ 静态/动态 │ span<T, N>(编译期大小)/ span<T>(运行时大小)│
* └────────────────┴──────────────────────────────────────────────┘
*/
// ❌ C++17:接受数组的函数需要多个重载或模板
void process_old(const int* data, size_t size) { /* C风格 */ }
void process_old(const std::vector<int>& v) { /* vector */ }
template<size_t N> void process_old(const std::array<int, N>& a) { /* array */ }
// ✅ C++20:span 统一所有连续容器
void process(std::span<const int> data) {
for (int val : data) {
std::cout << val << " ";
}
std::cout << "\n";
// 丰富的操作
auto first3 = data.first(3); // 前3个元素的子 span
auto last2 = data.last(2); // 后2个元素的子 span
auto mid = data.subspan(1, 3); // 从索引1开始取3个
std::cout << "Size: " << data.size() << "\n";
std::cout << "Empty: " << data.empty() << "\n";
std::cout << "Front: " << data.front() << "\n";
std::cout << "Back: " << data.back() << "\n";
std::cout << "[2]: " << data[2] << "\n";
}
void span_usage() {
// 同一个函数接受所有连续容器
int c_arr[] = {1, 2, 3, 4, 5};
std::vector<int> vec = {10, 20, 30};
std::array<int, 4> arr = {100, 200, 300, 400};
process(c_arr); // ✅ C 数组
process(vec); // ✅ vector
process(arr); // ✅ array
}
// 可修改的 span
void modify(std::span<int> data) {
for (auto& val : data) {
val *= 2;
}
}
// 静态大小的 span(编译期已知大小)
void fixed_size(std::span<int, 3> triple) {
// 保证恰好3个元素
std::cout << triple[0] + triple[1] + triple[2] << "\n";
}
void static_span_demo() {
int arr[] = {1, 2, 3, 4, 5};
// fixed_size(arr); // ❌ 大小不匹配(5 != 3)
fixed_size({arr, 3}); // ✅ 取前3个
std::span<int, 3> s(arr, 3); // ✅ 显式构造
fixed_size(s);
}
// 二维数据处理
void process_matrix(std::span<const std::span<const int>> rows) {
for (auto row : rows) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << "\n";
}
}
⏰ 9. 日历与时区(<chrono> 增强)
C++
#include <chrono>
#include <iostream>
namespace chrono = std::chrono;
void calendar_example() {
// ═══ 日历类型 ═══
using namespace chrono;
// 创建日期
auto today = year{2024}/December/25; // 2024-12-25
auto same = year_month_day{year{2024}, month{12}, day{25}};
auto also = 2024y/12/25d; // 字面量语法
// 日期查询
std::cout << "Valid: " << today.ok() << "\n"; // true
std::cout << "Year: " << (int)today.year() << "\n";
std::cout << "Month: " << (unsigned)today.month() << "\n";
std::cout << "Day: " << (unsigned)today.day() << "\n";
// 星期几
auto wd = weekday{sys_days{today}};
std::cout << "Weekday: " << wd << "\n"; // Wed
// 特殊日期
auto first_monday = year{2024}/January/Monday[1]; // 2024年1月第一个周一
auto last_friday = year{2024}/March/Friday[last]; // 2024年3月最后一个周五
// 日期运算
auto next_month = today + months{1};
auto next_year = today + years{1};
// 年月的最后一天
auto end_of_feb = year{2024}/February/last; // 2024-02-29(闰年)
}
void timezone_example() {
using namespace chrono;
// 当前时间(UTC)
auto now_utc = system_clock::now();
// 转换为本地时间
auto now_local = zoned_time{current_zone(), now_utc};
std::cout << "Local: " << now_local << "\n";
// 指定时区
auto tokyo = zoned_time{"Asia/Tokyo", now_utc};
auto nyc = zoned_time{"America/New_York", now_utc};
std::cout << "Tokyo: " << tokyo << "\n";
std::cout << "NYC: " << nyc << "\n";
// 时区转换
auto meeting_nyc = zoned_time{"America/New_York",
local_days{2024y/June/15} + 14h}; // 纽约时间 6月15日 14:00
auto meeting_tokyo = zoned_time{"Asia/Tokyo", meeting_nyc};
std::cout << "Meeting in Tokyo: " << meeting_tokyo << "\n";
}
// 高精度计时增强
void timing_enhanced() {
using namespace chrono;
// 日/周/月/年 时间单位
auto one_day = days{1};
auto two_weeks = weeks{2};
// hh_mm_ss:时分秒解析
auto duration = 3723s; // 3723 秒
hh_mm_ss hms{duration};
std::cout << hms.hours() << "h "
<< hms.minutes() << "m "
<< hms.seconds() << "s\n"; // 1h 2m 3s
}
🧵 10. 并发增强
10.1 std::jthread 与协作式取消:
C++
#include <thread>
#include <stop_token>
// ❌ C++17 std::thread 的问题:
// 1. 忘记 join() 会 std::terminate
// 2. 无标准化的取消机制
// ✅ C++20 std::jthread:自动 join + 支持取消
void jthread_example() {
// 析构时自动 join(J = Joining Thread)
{
std::jthread t([]() {
std::cout << "Working...\n";
});
// 作用域结束 → 自动 join,不会 terminate
}
// 协作式取消
std::jthread worker([](std::stop_token stoken) {
while (!stoken.stop_requested()) { // 检查是否被请求停止
std::cout << "Working...\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << "Stopped gracefully.\n";
});
std::this_thread::sleep_for(std::chrono::milliseconds(350));
worker.request_stop(); // 请求线程停止
// worker 析构时自动 join
// stop_callback:注册取消时的回调
std::jthread t2([](std::stop_token st) {
std::stop_callback callback(st, []() {
std::cout << "Cleanup on cancel!\n";
});
while (!st.stop_requested()) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
});
t2.request_stop(); // 触发回调 + 线程退出
}
10.2 新增同步原语:
C++
#include <semaphore>
#include <latch>
#include <barrier>
// ═══ std::counting_semaphore / std::binary_semaphore ═══
void semaphore_example() {
// 限制并发数为 3
std::counting_semaphore<3> sem(3);
auto worker = [&sem](int id) {
sem.acquire(); // P 操作(阻塞直到 > 0,然后 -1)
std::cout << "Thread " << id << " working\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
sem.release(); // V 操作(+1)
};
std::vector<std::jthread> threads;
for (int i = 0; i < 10; ++i)
threads.emplace_back(worker, i);
// 最多 3 个线程同时执行
// binary_semaphore = counting_semaphore<1>
std::binary_semaphore gate(0); // 初始值 0(阻塞)
std::jthread t([&gate]() {
gate.acquire(); // 等待信号
std::cout << "Gate opened!\n";
});
gate.release(); // 发送信号
}
// ═══ std::latch(一次性屏障)═══
void latch_example() {
constexpr int N = 5;
std::latch start_latch(1); // 所有线程等待开始信号
std::latch done_latch(N); // 主线程等待所有子线程完成
std::vector<std::jthread> workers;
for (int i = 0; i < N; ++i) {
workers.emplace_back([&, i]() {
start_latch.wait(); // 等待开始
std::cout << "Worker " << i << " running\n";
done_latch.count_down(); // 标记完成
});
}
start_latch.count_down(); // 发出开始信号
done_latch.wait(); // 等待所有完成
std::cout << "All workers done!\n";
}
// ═══ std::barrier(可复用屏障)═══
void barrier_example() {
constexpr int N = 3;
int phase = 0;
std::barrier sync_point(N, [&]() noexcept {
// 所有线程到达后执行的回调
++phase;
std::cout << "=== Phase " << phase << " complete ===\n";
});
auto worker = [&](int id) {
for (int i = 0; i < 3; ++i) {
std::cout << "Thread " << id << " phase " << i << "\n";
sync_point.arrive_and_wait(); // 等待所有线程到达
}
};
std::vector<std::jthread> threads;
for (int i = 0; i < N; ++i)
threads.emplace_back(worker, i);
}
// ═══ std::atomic 增强 ═══
// atomic<shared_ptr>、atomic<weak_ptr>、atomic 等待/通知
void atomic_wait_example() {
std::atomic<int> value{0};
std::jthread producer([&value]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
value.store(42);
value.notify_one(); // ✅ C++20:原子通知
});
value.wait(0); // ✅ C++20:原子等待(阻塞直到值不等于 0)
std::cout << "Got: " << value.load() << "\n"; // 42
}
🏗️ 11. 语言特性增强
11.1 指定初始化器(Designated Initializers):
C++
struct Config {
int width = 800;
int height = 600;
bool fullscreen = false;
std::string title = "App";
double fps = 60.0;
};
void designated_init() {
// ✅ C++20:按名称初始化成员(必须按声明顺序)
Config cfg = {
.width = 1920,
.height = 1080,
.fullscreen = true,
// title 使用默认值 "App"
.fps = 144.0
};
// 极大提高可读性(特别是有很多参数时)
// 对比 C++17:Config cfg{1920, 1080, true, "App", 144.0}; // 哪个是什么?
// ⚠️ 顺序必须与声明一致
// Config bad = {.height = 100, .width = 200}; // ❌ 顺序错误
// ⚠️ 不能混用指定和非指定
// Config bad2 = {1920, .fullscreen = true}; // ❌
}
11.2 using enum:
C++
enum class Color { Red, Green, Blue };
enum class Permission { Read = 1, Write = 2, Execute = 4 };
void using_enum_example() {
// ❌ C++17:枚举值需要带前缀
Color c = Color::Red;
// ✅ C++20:using enum 引入所有枚举值
using enum Color;
Color c2 = Red; // ✅ 直接使用
// 常用于 switch
switch (c2) {
using enum Color;
case Red: std::cout << "Red\n"; break;
case Green: std::cout << "Green\n"; break;
case Blue: std::cout << "Blue\n"; break;
}
}
11.3 模板增强:
C++
// ═══ 简写函数模板(Abbreviated Function Templates)═══
// 函数参数中的 auto 等价于模板参数
void print(auto const& value) {
std::cout << value << "\n";
}
// 等价于:
// template <typename T> void print(const T& value) { ... }
// 带约束的简写
void process(std::integral auto x) {
std::cout << x * 2 << "\n";
}
// ═══ 非类型模板参数增强 ═══
// C++20 允许浮点数和字面量类类型作为非类型模板参数
template <double Value>
struct FloatConstant {
static constexpr double value = Value;
};
FloatConstant<3.14> pi_const;
// 字面量类类型作为 NTTP
struct FixedString {
char data[32]{};
consteval FixedString(const char* s) {
for (int i = 0; s[i]; ++i) data[i] = s[i];
}
};
template <FixedString Name>
struct NamedType {
static constexpr auto name = Name.data;
};
NamedType<"velocity"> vel; // ✅ 字符串字面量作为模板参数!
static_assert(vel.name[0] == 'v');
// ═══ Lambda 增强 ═══
// 模板 Lambda
auto generic = []<typename T>(std::vector<T>& v) {
// 可以使用 T
T sum{};
for (const auto& elem : v) sum += elem;
return sum;
};
// Lambda 可默认构造和赋值(无状态 Lambda)
auto cmp = [](int a, int b) { return a < b; };
decltype(cmp) another; // ✅ C++20 允许默认构造无状态 Lambda
// 显式 this 参数(Deducing this, 实际是 C++23,但概念在 C++20 讨论)
11.4 [[likely]] 与 [[unlikely]]:
C++
// 向编译器提示分支的可能性,帮助优化
int process_request(int type) {
if (type == 0) [[unlikely]] {
// 错误处理路径(极少执行)
return handle_error();
}
if (type == 1) [[likely]] {
// 热路径(最常执行)
return fast_path();
}
return default_path();
}
// switch 中使用
void dispatch(int code) {
switch (code) {
[[likely]] case 200: handle_ok(); break;
[[unlikely]] case 500: handle_error(); break;
default: handle_other(); break;
}
}
11.5 [[no_unique_address]]:
C++
// 允许空成员不占用存储空间(Empty Base Optimization 的成员版)
struct Empty {};
// ❌ C++17:空成员仍占用至少 1 字节
struct OldHolder {
Empty e; // 1 字节(加上对齐可能更多)
int value; // 4 字节
// sizeof = 8(含对齐)
};
// ✅ C++20:空成员可以不占空间
struct NewHolder {
[[no_unique_address]] Empty e; // 0 字节!
int value; // 4 字节
// sizeof = 4
};
// 实际应用:存储无状态的分配器/比较器/删除器时节省空间
template <typename T, typename Deleter = std::default_delete<T>>
class CompactPtr {
T* ptr_;
[[no_unique_address]] Deleter deleter_; // 无状态时不占空间
public:
~CompactPtr() { deleter_(ptr_); }
};
// sizeof(CompactPtr<int>) == sizeof(int*) // ✅ 与裸指针一样大
11.6 立即函数中的 consteval 与 is_constant_evaluated():
C++
#include <type_traits>
constexpr int compute(int x) {
if (std::is_constant_evaluated()) {
// 编译期路径(可以做更安全但更慢的实现)
return x * x;
} else {
// 运行期路径(可以用硬件加速等)
return x * x; // 实际可调用非 constexpr 函数
}
}
void is_ce_demo() {
constexpr int a = compute(5); // 走编译期路径
int x = 5;
int b = compute(x); // 走运行期路径
}
📚 12. 标准库其他增强
12.1 std::source_location:
C++
#include <source_location>
// 替代 __FILE__、__LINE__、__func__
void log(const std::string& message,
const std::source_location& loc = std::source_location::current()) {
std::cout << loc.file_name() << ":"
<< loc.line() << " ["
<< loc.function_name() << "] "
<< message << "\n";
}
void source_location_demo() {
log("Something happened");
// 输出:main.cpp:42 [source_location_demo] Something happened
// 比宏更安全、更灵活、支持默认参数
}
12.2 std::bit_cast:
C++
#include <bit>
// 类型安全的位级重新解释(替代 reinterpret_cast / memcpy)
void bit_cast_example() {
float f = 3.14f;
// ❌ reinterpret_cast 是未定义行为
// int bad = *reinterpret_cast<int*>(&f);
// ✅ C++20:std::bit_cast
auto i = std::bit_cast<uint32_t>(f);
std::cout << std::format("Float {:.2f} = 0x{:08X}\n", f, i);
// 反向转换
float f2 = std::bit_cast<float>(i);
// constexpr 安全
static_assert(std::bit_cast<uint32_t>(1.0f) == 0x3F800000);
}
// 其他 <bit> 工具
void bit_utils() {
unsigned x = 0b0101'1010;
std::cout << std::popcount(x) << "\n"; // 4(1的个数)
std::cout << std::countl_zero(x) << "\n"; // 前导零数量
std::cout << std::countr_zero(x) << "\n"; // 尾随零数量
std::cout << std::has_single_bit(8u) << "\n"; // true(是2的幂)
std::cout << std::bit_ceil(5u) << "\n"; // 8(向上取2的幂)
std::cout << std::bit_floor(5u) << "\n"; // 4(向下取2的幂)
std::cout << std::bit_width(5u) << "\n"; // 3(表示5需要的位数)
std::cout << std::rotl(x, 2) << "\n"; // 循环左移
std::cout << std::rotr(x, 2) << "\n"; // 循环右移
}
12.3 容器与字符串增强:
C++
void container_enhancements() {
// ═══ starts_with / ends_with(string / string_view)═══
std::string url = "https://example.com/api/data";
if (url.starts_with("https://")) {
std::cout << "Secure URL\n";
}
if (url.ends_with("/data")) {
std::cout << "Data endpoint\n";
}
// ═══ contains()(关联容器)═══
std::map<std::string, int> scores = {{"Alice", 90}, {"Bob", 85}};
// ❌ C++17
if (scores.count("Alice") > 0) { /* ... */ }
if (scores.find("Alice") != scores.end()) { /* ... */ }
// ✅ C++20
if (scores.contains("Alice")) {
std::cout << "Found Alice\n";
}
// set 也支持
std::set<int> s = {1, 2, 3, 4, 5};
std::cout << s.contains(3) << "\n"; // true
// ═══ std::erase / std::erase_if(统一容器擦除)═══
std::vector<int> v = {1, 2, 3, 2, 4, 2, 5};
// ❌ C++17:erase-remove idiom
v.erase(std::remove(v.begin(), v.end(), 2), v.end());
// ✅ C++20:一行搞定
std::vector<int> v2 = {1, 2, 3, 2, 4, 2, 5};
std::erase(v2, 2); // 删除所有 2
std::erase_if(v2, [](int x) { return x > 3; }); // 删除所有大于3的
// 适用于所有标准容器
std::list<int> lst = {1, 2, 3, 4, 5};
std::erase_if(lst, [](int x) { return x % 2 == 0; });
std::map<std::string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}};
std::erase_if(m, [](const auto& pair) { return pair.second < 2; });
}
// ═══ std::to_array ═══
void to_array_example() {
// C 数组 → std::array
auto arr = std::to_array({1, 2, 3, 4, 5}); // std::array<int, 5>
// 字符串数组
auto strs = std::to_array<std::string>({"hello", "world"});
// 移动语义
auto ptrs = std::to_array({
std::make_unique<int>(1),
std::make_unique<int>(2)
});
}
// ═══ std::midpoint / std::lerp ═══
#include <numeric>
#include <cmath>
void math_additions() {
// 安全的中点计算(不会溢出)
int a = 2'000'000'000, b = 2'000'000'000;
int mid = std::midpoint(a, b); // ✅ 正确,不会整数溢出
// (a + b) / 2 会溢出!
// 线性插值
double result = std::lerp(0.0, 10.0, 0.3); // 3.0
// lerp(a, b, t) = a + t * (b - a)
}
// ═══ std::make_shared 支持数组(C++20)═══
void shared_array() {
auto arr = std::make_shared<int[]>(10); // 10个int的数组
arr[0] = 42;
auto arr2 = std::make_shared<int[5]>(); // 固定大小5
}
12.4 std::atomic_ref:
C++
#include <atomic>
// 对非原子对象提供原子操作
void atomic_ref_example() {
int normal_int = 0; // 普通 int,非 atomic
auto increment = [&normal_int]() {
// 创建原子引用来安全操作
std::atomic_ref<int> ref(normal_int);
for (int i = 0; i < 10000; ++i) {
ref.fetch_add(1, std::memory_order_relaxed);
}
};
std::jthread t1(increment);
std::jthread t2(increment);
t1.join();
t2.join();
std::cout << normal_int << "\n"; // 20000(精确)
// 用途:对结构体中的某个字段进行原子操作
// 无需将整个结构体变为 atomic
}
📊 13. 综合特性速查表
C++
┌──────────────────────┬───────────────────────────────────────────────────────┐
│ 特性分类 │ 具体特性 │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 四大旗舰 │ │
│ Concepts │ concept 定义, requires 表达式, 约束重载决议 │
│ Ranges │ 管道语法, views 惰性视图, 投影, ranges 算法 │
│ Coroutines │ co_await, co_yield, co_return, promise_type │
│ Modules │ export module, import, 模块分区 │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 比较运算 │ <=> 三路比较, auto operator<=>(...) = default │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 编译期计算 │ constexpr 虚函数/动态分配/try-catch/vector/string │
│ │ consteval 强制编译期 │
│ │ constinit 保证静态初始化 │
│ │ is_constant_evaluated() │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 格式化 │ std::format 类 Python 格式化 │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 视图类型 │ std::span 连续内存视图 │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 时间日期 │ 日历类型(year/month/day)、时区(zoned_time) │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 并发 │ jthread + stop_token 协作取消 │
│ │ semaphore, latch, barrier │
│ │ atomic::wait/notify, atomic_ref │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 语法增强 │ 指定初始化器 {.x=1} │
│ │ using enum │
│ │ 简写函数模板 (auto 参数) │
│ │ 非类型模板参数 (浮点/字面量类) │
│ │ 模板 Lambda []<typename T>(...) │
│ │ constexpr Lambda │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 属性 │ [[likely]], [[unlikely]] │
│ │ [[no_unique_address]] │
├──────────────────────┼───────────────────────────────────────────────────────┤
│ 标准库工具 │ source_location, bit_cast, bit 操作 │
│ │ starts_with/ends_with, contains() │
│ │ std::erase/erase_if, to_array │
│ │ midpoint, lerp │
│ │ make_shared<T[]> │
└──────────────────────┴───────────────────────────────────────────────────────┘
💡 与 C++11/14/17 的演进关系
C++
┌─────────────────────────────────────────────────────────────────────────┐
│ C++17 → C++20 演进关系 │
├──────────────────────────┬──────────────────────────────────────────────┤
│ C++17 及之前的痛点 │ C++20 的改进 │
├──────────────────────────┼──────────────────────────────────────────────┤
│ 模板错误信息不可读 │ ✅ Concepts:约束显式化,错误信息清晰 │
│ SFINAE/enable_if 复杂 │ │
├──────────────────────────┼──────────────────────────────────────────────┤
│ 算法需要 begin/end 对 │ ✅ Ranges:直接传容器 + 惰性管道组合 │
│ 无惰性求值/管道组合 │ │
├──────────────────────────┼──────────────────────────────────────────────┤
│ 无标准协程支持 │ ✅ Coroutines:co_await/yield/return │
├──────────────────────────┼──────────────────────────────────────────────┤
│ #include 编译慢/宏污染 │ ✅ Modules:预编译、无宏泄漏 │
├──────────────────────────┼──────────────────────────────────────────────┤
│ 手写6个比较运算符 │ ✅ <=> 一行生成全部 │
├──────────────────────────┼──────────────────────────────────────────────┤
│ constexpr 不能用 vector │ ✅ constexpr 动态分配 + STL 容器 │
├──────────────────────────┼──────────────────────────────────────────────┤
│ printf 不安全,cout 太慢 │ ✅ std::format 安全+快速+可读 │
├──────────────────────────┼──────────────────────────────────────────────┤
│ 接受数组参数需多个重载 │ ✅ std::span 统一连续容器 │
├──────────────────────────┼──────────────────────────────────────────────┤
│ 无标准日期/时区 API │ ✅ chrono 日历 + 时区 │
├──────────────────────────┼──────────────────────────────────────────────┤
│ thread 忘记 join 则崩溃 │ ✅ jthread 自动 join + 协作取消 │
│ 无标准化取消机制 │ │
├──────────────────────────┼──────────────────────────────────────────────┤
│ erase-remove 惯用法繁琐 │ ✅ std::erase / std::erase_if 一行删除 │
├──────────────────────────┼──────────────────────────────────────────────┤
│ map.count / find 判断存在 │ ✅ map.contains() 语义清晰 │
├──────────────────────────┼──────────────────────────────────────────────┤
│ 聚合初始化不知道哪个是哪个 │ ✅ 指定初始化器 {.width=100, .height=200} │
└──────────────────────────┴──────────────────────────────────────────────┘
💡 关键实践原则
- 用 Concepts 替代 SFINAE / enable_if
- 模板约束显式化,错误信息对人友好
- 优先使用标准库预定义 concepts(
std::integral、std::invocable等) - 简写语法
void f(std::integral auto x)最为简洁
- 用 Ranges 管道替代手写循环和 begin/end 对
views::filter | views::transform | views::take声明式编程- 惰性求值 + 零分配,性能不打折
- 投影参数避免包装 Lambda
- 用
<=>一行完成所有比较运算符- 新写的类都应该使用
auto operator<=>(...) const = default - 自定义逻辑时注意同时声明
operator==
- 新写的类都应该使用
- 用
std::format替代 printf 和 iostream 拼接- 类型安全 + 高性能 + 可读性最佳
- 为自定义类型特化
std::formatter
- 用
std::span统一接受连续内存参数- 替代
(T* ptr, size_t len)和const vector<T>& - ⚠️ 注意生命周期(同
string_view)
- 替代
- 用
std::jthread+stop_token替代std::thread- 自动 join 避免忘记导致 terminate
- 标准化的协作式取消机制
- 充分利用
constexpr的全面扩展- 编译期使用
vector、string、算法 consteval强制编译期计算,constinit防止静态初始化顺序问题
- 编译期使用
- 用
contains()、starts_with()、std::erase_if简化常见操作- 这些 API 让代码意图更直接,减少样板代码
总结:
C++20 是自 C++11 以来最具变革意义的版本,四大旗舰特性从根本上改变了 C++ 的编程范式。Concepts 让模板编程从"黑魔法"走向"可读可维护";Ranges 将函数式编程的管道组合引入 C++,实现了声明式的数据处理;Coroutines 为异步编程和生成器提供了底层支持;Modules 开启了告别 #include 的新时代。
除四大旗舰外,三路比较运算符 消灭了样板比较代码,constexpr 的全面扩展 让编译期计算达到了前所未有的能力,**std::format**终结了格式化输出的安全与性能之争,并发原语 (jthread、semaphore、latch、barrier)补齐了多线程编程的基础设施。
C++20 的设计目标是"让简单的事情简单,让复杂的事情成为可能,让错误的事情难以发生"。掌握 C++20 不仅是技术能力的提升,更是编程思维的飞跃------从过程式到声明式,从运行期到编译期,从手动管理到自动安全。它为 C++ 在系统编程、高性能计算、嵌入式、游戏引擎等领域继续保持统治地位奠定了坚实的现代化基础。