C++20 是继 C++11 以来最大的版本更新,四大天王:Concepts、Ranges、Coroutines、Modules。
给模板参数加约束,让模板报错不再是天书:
1
2
3
4
5
6
7
8
9
10
11
| // 定义一个概念:T 必须是可加的
template<typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::same_as<T>;
};
// 使用概念约束模板参数
template<Addable T>
T add(T a, T b) {
return a + b;
}
|
四种约束形式:
- 简单约束:
sizeof(T) == 4 - 类型约束:
std::integral<T> - 复合约束:
{ expr } -> 类型概念 - 嵌套约束:
requires { ... }
1
2
3
4
5
6
7
| template<typename T>
concept Container = requires(T c) {
c.begin(); // 必须有 begin()
c.end(); // 必须有 end()
typename T::value_type; // 必须有 value_type 类型
{ c.size() } -> std::convertible_to<size_t>;
};
|
<concepts> 头文件里提供了一堆:
std::same_as<T, U> — 类型相同std::derived_from<Derived, Base> — 派生关系std::convertible_to<From, To> — 可转换std::integral<T> — 整型std::floating_point<T> — 浮点型std::ranges::range<T> — 范围
彻底改变 STL 算法的写法,支持链式调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #include <ranges>
std::vector<int> nums = {1, 2, 3, 4, 5, 6};
// C++17:嵌套调用,可读性差
// C++20:管道式,从左到右读
auto result = nums
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 2; })
| std::views::take(3);
for (int x : result) {
std::cout << x << " "; // 4 8 12
}
|
| 适配器 | 作用 |
|---|
views::filter | 过滤 |
views::transform | 转换映射 |
views::take(n) | 取前 n 个 |
views::drop(n) | 跳过前 n 个 |
views::reverse | 反转 |
views::keys / values | 取 pair 的 key/value |
views::join | 扁平化 |
views::split | 分割 |
views 不会立即计算,只有在迭代时才生成元素,性能更好。
函数可以暂停和恢复,异步编程神器:
1
2
3
| co_await // 暂停,等待异步操作
co_return // 返回值并结束
co_yield // 返回一个值并暂停,下次从这里继续
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| generator<int> fibonacci(int n) {
int a = 0, b = 1;
for (int i = 0; i < n; ++i) {
co_yield b; // 返回 b,暂停在这里
int next = a + b;
a = b;
b = next;
}
}
// 使用
for (int x : fibonacci(10)) {
std::cout << x << " ";
}
|
1
2
3
4
| task<int> fetchData() {
auto data = co_await asyncLoad(); // 暂停,不阻塞线程
co_return data.size();
}
|
注意:标准库只提供了协程机制,没有提供具体的协程类型(task/generator),需要自己实现或用第三方库(如 cppcoro)。C++23 才补了 std::generator。
告别 #include,解决头文件地狱:
1
2
3
4
5
6
7
8
9
10
| // math.ixx (模块接口文件)
export module math;
export int add(int a, int b) {
return a + b;
}
export namespace math_utils {
int mul(int a, int b) { return a * b; }
}
|
1
2
3
4
5
6
| import math;
int main() {
add(1, 2);
math_utils::mul(3, 4);
}
|
优点:
- 编译速度大幅提升(只编译一次)
- 没有宏污染
- 模块内部的东西默认不导出,封装更好
俗称「飞船运算符」,一次比较得出大小关系:
1
2
3
4
5
6
7
8
9
| struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // 自动生成所有比较运算符
};
Point a{1, 2}, b{3, 4};
a < b; // OK,自动生成
a >= b; // OK
a == b; // OK
|
返回类型有三种:
std::strong_ordering — 强序(完全可比)std::weak_ordering — 弱序std::partial_ordering — 偏序(可能不可比,比如 NaN)
像 C 语言那样按名字初始化结构体成员:
1
2
3
4
5
6
7
8
9
10
11
| struct Point {
int x = 0;
int y = 0;
int z = 0;
};
Point p {
.x = 10,
.z = 30
// y 用默认值 0
};
|
注意:顺序必须和声明顺序一致,不能跳着来。
必须在编译期执行,否则报错:
1
2
3
4
5
6
7
| consteval int sqr(int n) {
return n * n;
}
constexpr int a = sqr(10); // OK,编译期
int b = sqr(20); // OK,编译期常量
// int x = 5; sqr(x); // ERROR,x 不是编译期常量
|
保证变量在编译期初始化,避免静态初始化顺序问题:
1
| constinit int global = compute_at_compile_time();
|
Python 风格的格式化字符串,终于不用 printf 或 stringstream 了:
1
2
3
4
5
6
7
8
9
10
11
12
| #include <format>
std::string s = std::format("Hello, {}! You are {} years old.", "Tom", 25);
// "Hello, Tom! You are 25 years old."
// 按位置
std::format("{1} {0}", "world", "Hello"); // "Hello world"
// 格式说明
std::format("{:>10}", 42); // 右对齐,占10位
std::format("{:08b}", 42); // 8位二进制,前导零
std::format("{:.2f}", 3.14159); // 保留2位小数
|
连续内存的视图,类似 string_view 但是通用的:
1
2
3
4
5
6
7
8
9
| void printArray(std::span<int> arr) {
for (int x : arr) std::cout << x << " ";
}
int a[] = {1, 2, 3};
printArray(a); // 原生数组
std::vector v = {4, 5, 6};
printArray(v); // vector
|
| 特性 | 说明 |
|---|
std::jthread | 自动 join 的线程,支持停止令牌 |
std::stop_token | 协作式线程中断 |
std::source_location | 获取当前代码位置(文件名、行号) |
std::bit_cast | 安全的类型双关,替代 memcpy |
std::midpoint / std::lerp | 中点和线性插值 |
[[likely]] / [[unlikely]] | 给编译器分支预测提示 |
| 范围 for 初始化语句 | for (auto it = v.begin(); auto x : v) |
1
2
3
4
5
6
| // likely/unlikely 示例
if (likely(n > 0)) {
// 大概率走这里,编译器优化
} else {
// 小概率
}
|
C++20 是改变编程范式的大版本:
- Concepts:模板终于好用了,报错终于能看懂了
- Ranges:STL 算法的现代写法,链式调用爽
- Coroutines:异步编程基础设施
- Modules:告别头文件,编译起飞
- format / span / jthread:各种小确幸