24、【C++】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::vectorstd::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++语言的重要里程碑。合理应用这些特性可使代码更简洁、高效、易维护。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐