24、【C++】C++20常用特性
/ 定义概念:Numeric是整数或浮点数// 使用概念约束模板参数add(1, 2);// 正确:int是Numeric// 正确:double是Numeric// 错误:string不满足Numericreturn 0;默认实现:按成员顺序比较(如Point先比较x,再比较y)。自定义实现cmp!为自定义类型重载int y;
24、【C++】C++20常用特性
目录
- 一、核心语言特性
- 二、标准库新增特性
- 三、C++20特性应用场景
- 四、编译器支持与迁移
一、核心语言特性
1.1 概念(Concepts)
1.1.1 概念定义与约束
概念是编译期谓词,用于约束模板参数类型:
#include <concepts>
// 定义概念:Numeric是整数或浮点数
template <typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
// 使用概念约束模板参数
template <Numeric T>
T add(T a, T b) {
return a + b;
}
int main() {
add(1, 2); // 正确:int是Numeric
add(1.5, 2.5); // 正确:double是Numeric
// add("a", "b"); // 错误:string不满足Numeric
return 0;
}
1.1.2 标准概念(std::integral等)
C++20提供丰富的标准概念(定义在<concepts>中):
std::integral<T>:整数类型(int、char等)。std::floating_point<T>:浮点类型(float、double等)。std::ranges::range<T>:T是范围(支持begin()/end())。std::convertible_to<T, U>:T可转换为U。
1.1.3 模板错误信息优化
概念使模板错误信息更易读,例如调用add("a", "b")时:
- 无概念:错误信息涉及复杂的SFINAE失败。
- 有概念:直接提示"string不满足Numeric约束"。
1.2 范围(Ranges)
1.2.1 范围概念(Range、View)
范围是可迭代的序列,满足std::ranges::range概念,包含:
- 可修改范围:
std::ranges::output_range。 - 视图(View):无所有权、惰性计算的范围(如
std::views::filter)。
1.2.2 管道操作与视图适配器
范围支持管道(|)操作,串联多个视图适配器:
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// 管道操作:过滤偶数 → 平方 → 打印
auto even_squares = v | std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });
for (int x : even_squares) {
std::cout << x << " "; // 输出:4 16
}
return 0;
}
1.3 协程(Coroutines)
1.3.1 协程基础(co_await、co_yield、co_return)
协程是可暂停/恢复的函数,使用关键字:
co_await expr:暂停等待expr完成。co_yield expr:返回中间结果,保持函数状态。co_return expr:返回最终结果,结束协程。
1.3.2 协程返回类型(std::generator)
std::generator<T>是简化的协程返回类型,生成T序列:
#include <coroutine>
#include <generator>
// 生成从start开始的整数序列
std::generator<int> counter(int start) {
while (true) {
co_yield start++; // 返回当前值并暂停
}
}
int main() {
auto gen = counter(1);
auto it = gen.begin();
std::cout << *it++ << std::endl; // 1
std::cout << *it++ << std::endl; // 2
return 0;
}
1.4 模块(Modules)
1.4.1 模块声明与导入
模块替代头文件,通过export module声明,import导入:
// module math.ixx(模块接口文件)
export module math;
export int add(int a, int b) {
return a + b;
}
// main.cpp
import math;
import <iostream>;
int main() {
std::cout << add(1, 2) << std::endl; // 3
return 0;
}
1.4.2 模块与头文件的区别
| 特性 | 模块 | 头文件 |
|---|---|---|
| 编译 | 一次编译,多处导入 | 每次包含都重新编译 |
| 命名冲突 | 模块内私有,导出需显式声明 | 全局命名空间污染 |
| 依赖 | 显式导入依赖模块 | 通过#include隐式依赖 |
1.5 三路比较运算符(<=>)
1.5.1 自动生成比较运算符
<=>(太空船运算符)自动生成==、!=、<、<=、>、>=:
struct Point {
int x;
int y;
// 自动生成所有比较运算符
auto operator<=>(const Point&) const = default;
};
int main() {
Point a{1, 2}, b{3, 4};
bool less = a < b; // 等价于(a.x < b.x) || (a.x == b.x && a.y < b.y)
return 0;
}
1.5.2 默认与自定义实现
- 默认实现:按成员顺序比较(如
Point先比较x,再比较y)。 - 自定义实现:
auto operator<=>(const Point& other) const { if (auto cmp = x <=> other.x; cmp != 0) return cmp; return y <=> other.y; }
1.6 constexpr增强
1.6.1 constexpr动态内存分配
C++20允许constexpr new/delete,编译期动态内存管理:
constexpr int* create_int(int value) {
return new int(value); // 编译期分配
}
constexpr void destroy_int(int* p) {
delete p; // 编译期释放
}
constexpr int func() {
int* p = create_int(42);
int val = *p;
destroy_int(p);
return val;
}
constexpr int x = func(); // 编译期计算x=42
1.6.2 constexpr容器(vector、string)
C++20允许constexpr std::vector和std::string:
constexpr std::vector<int> create_vector() {
std::vector<int> v;
v.push_back(1);
v.push_back(2);
return v;
}
constexpr auto v = create_vector(); // 编译期创建vector,v={1,2}
1.7 其他语言特性
1.7.1 consteval函数
consteval函数必须在编译期计算,比constexpr更严格:
consteval int add(int a, int b) { return a + b; }
constexpr int x = add(1, 2); // 编译期计算x=3
int y = add(1, 2); // 编译期计算y=3
// int z = add(get_user_input(), 2); // 错误:运行时参数无法编译期计算
1.7.2 指定初始化器
按成员名初始化结构体,顺序无关:
struct Person {
std::string name;
int age;
};
Person p{.age=30, .name="Alice"}; // 正确:按成员名初始化
1.7.3 using enum
导入枚举成员到当前作用域:
enum class Color { Red, Green, Blue };
int main() {
using enum Color; // 导入Color成员
Color c = Red; // 直接使用Red,无需Color::Red
return 0;
}
二、标准库新增特性
2.1 范围库(std::ranges)
2.1.1 范围算法(ranges::sort等)
范围算法直接接受范围,无需传递迭代器对:
#include <ranges>
#include <vector>
int main() {
std::vector<int> v = {3, 1, 2};
std::ranges::sort(v); // 直接排序v,无需v.begin()/v.end()
return 0;
}
2.1.2 视图(views::filter、views::transform)
视图是惰性计算的范围适配器,不修改原始数据:
// 过滤偶数并平方(惰性计算,仅在遍历时有实际操作)
auto even_squares = v | std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });
2.2 格式化库(std::format)
2.2.1 基本用法与占位符
std::format支持类型安全的格式化:
#include <format>
#include <string>
int main() {
std::string s = std::format("Hello, {}! Age: {}", "Alice", 30);
// s = "Hello, Alice! Age: 30"
return 0;
}
占位符格式:{index[:format-spec]},如{0:.2f}保留两位小数。
2.2.2 自定义类型格式化
为自定义类型重载std::formatter:
struct Point { int x; int y; };
template <>
struct std::formatter<Point> {
auto parse(std::format_parse_context& ctx) { return ctx.begin(); }
auto format(const Point& p, std::format_context& ctx) {
return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
}
};
std::string s = std::format("Point: {}", Point{1, 2}); // "Point: (1, 2)"
2.3 日期时间库(std::chrono扩展)
2.3.1 日历日期(year_month_day)
处理年/月/日:
#include <chrono>
namespace chrono = std::chrono;
int main() {
using namespace chrono;
year_month_day ymd = 2023y / October / 5d; // 2023年10月5日
std::cout << ymd.year() << "-" << ymd.month() << "-" << ymd.day() << std::endl;
return 0;
}
2.3.2 时区支持
处理不同时区的时间转换:
#include <chrono>
#include <chrono/tzdb>
int main() {
using namespace std::chrono;
auto now = system_clock::now();
auto nyc_time = zoned_time("America/New_York", now); // 纽约时间
std::cout << nyc_time << std::endl;
return 0;
}
2.4 std::span
2.4.1 连续序列视图
std::span表示连续内存序列的非所有权视图:
void print_span(std::span<int> data) {
for (int x : data) {
std::cout << x << " ";
}
}
int main() {
int arr[] = {1, 2, 3};
std::vector<int> v = {4, 5, 6};
print_span(arr); // 传递数组
print_span(v); // 传递vector
return 0;
}
2.4.2 与数组、vector的配合
span可处理数组、vector、std::array等,避免函数重载:
// 无需为数组和vector分别重载
void process_data(std::span<const int> data) { /* ... */ }
2.5 std::jthread
2.5.1 自动join与取消
std::jthread析构时自动join,避免忘记join导致程序崩溃:
#include <thread>
int main() {
std::jthread t([]() { /* 线程函数 */ });
// 析构时自动t.join(),无需手动调用
return 0;
}
2.5.2 与条件变量的配合
支持取消机制,通过std::stop_token响应取消请求:
std::jthread t([](std::stop_token st) {
while (!st.stop_requested()) {
// 循环处理,可被外部取消
}
});
t.request_stop(); // 请求线程停止
三、C++20特性应用场景
3.1 概念简化模板约束
用概念约束容器元素类型:
template <std::ranges::range R, std::integral T = std::ranges::range_value_t<R>>
void sum(R&& r) {
// 确保R的元素是整数类型
}
3.2 范围库优化数据流处理
处理日志数据:过滤错误日志 → 提取时间戳 → 排序:
auto error_logs = logs | views::filter([](const Log& log) { return log.level == Error; })
| views::transform([](const Log& log) { return log.timestamp; })
| views::sort;
3.3 协程实现异步生成器
异步读取文件行:
generator<std::string> read_lines_async(std::string path) {
std::ifstream file(path);
std::string line;
while (std::getline(file, line)) {
co_yield line; // 异步返回每行
}
}
3.4 模块提升编译速度
大型项目中,模块将编译时间从小时级降至分钟级,避免头文件重复包含。
四、编译器支持与迁移
4.1 主流编译器支持情况
- GCC:10+支持部分特性,11+支持大部分。
- Clang:12+支持主要特性。
- MSVC:VS 2019+支持模块、协程等核心特性。
4.2 混合模块与头文件
逐步迁移:新代码用模块,旧代码保留头文件,通过#include <header>在模块中使用头文件。
以上内容,全面覆盖了C++20的核心特性,包括概念、范围、协程、模块等革命性更新。C++20显著提升了代码的可读性、编译效率和开发效率,是C++语言的重要里程碑。合理应用这些特性可使代码更简洁、高效、易维护。
更多推荐



所有评论(0)