【GiraKoo】 C++20的新特性

C++20 是一个重大的更新版本,引入了许多革命性的特性,显著提升了语言的表达能力、类型安全性和开发效率。本文将详细介绍 C++20 的主要新特性,并提供丰富的代码示例。

目录

  • 目录
  • [🧠 一、核心语言特性](#🧠 一、核心语言特性 "#-%E4%B8%80%E6%A0%B8%E5%BF%83%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7")
    • [模块 (Modules)](#模块 (Modules) "#%E6%A8%A1%E5%9D%97-modules")
    • [概念 (Concepts)](#概念 (Concepts) "#%E6%A6%82%E5%BF%B5-concepts")
    • [协程 (Coroutines)](#协程 (Coroutines) "#%E5%8D%8F%E7%A8%8B-coroutines")
    • 三向比较(飞船运算符)
    • [指定初始化 (Designated Initializers)](#指定初始化 (Designated Initializers) "#%E6%8C%87%E5%AE%9A%E5%88%9D%E5%A7%8B%E5%8C%96-designated-initializers")
    • [范围 for 循环中的初始化语句](#范围 for 循环中的初始化语句 "#%E8%8C%83%E5%9B%B4-for-%E5%BE%AA%E7%8E%AF%E4%B8%AD%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E8%AF%AD%E5%8F%A5")
    • [[[likely]] 和 [[unlikely]] 属性](#[[likely]] 和 [[unlikely]] 属性 "#likely-%E5%92%8C-unlikely-%E5%B1%9E%E6%80%A7")
    • [constexpr 增强](#constexpr 增强 "#constexpr-%E5%A2%9E%E5%BC%BA")
    • [using enum 声明](#using enum 声明 "#using-enum-%E5%A3%B0%E6%98%8E")
    • 其他语言特性
  • [📚 二、标准库特性](#📚 二、标准库特性 "#-%E4%BA%8C%E6%A0%87%E5%87%86%E5%BA%93%E7%89%B9%E6%80%A7")
    • [1. 范围库 (Ranges Library)](#1. 范围库 (Ranges Library) "#1-%E8%8C%83%E5%9B%B4%E5%BA%93-ranges-library")
    • [2. std::format 格式化库](#2. std::format 格式化库 "#2-stdformat-%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%BA%93")
    • [3. std::span](#3. std::span "#3-stdspan")
    • [4. std::jthread (协作式中断线程)](#4. std::jthread (协作式中断线程) "#4-stdjthread-%E5%8D%8F%E4%BD%9C%E5%BC%8F%E4%B8%AD%E6%96%AD%E7%BA%BF%E7%A8%8B")
  • [🚀 总结](#🚀 总结 "#-%E6%80%BB%E7%BB%93")

🧠 一、核心语言特性

模块 (Modules)

目标: 取代传统的头文件(#include)机制,解决头文件包含导致的编译速度慢、宏污染、循环依赖等问题。

机制: 将代码划分为模块接口单元(.ixx, .cppm)和模块实现单元(.cpp)。接口单元使用 export module ModuleName; 导出声明;消费者使用 import ModuleName; 导入。

代码示例:

cpp 复制代码
// math_utils.ixx - 模块接口单元
export module math_utils;

// 导出函数声明
export int add(int a, int b);
export int multiply(int a, int b);

// 导出类
export class Calculator {
public:
    int calculate(int a, int b, char op);
private:
    int internal_helper(int x);  // 不导出,外部不可见
};

// 内部使用的函数,不导出
int internal_function() {
    return 42;
}
cpp 复制代码
// math_utils.cpp - 模块实现单元
module math_utils;

int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

int Calculator::calculate(int a, int b, char op) {
    switch(op) {
        case '+': return add(a, b);
        case '*': return multiply(a, b);
        default: return 0;
    }
}

int Calculator::internal_helper(int x) {
    return x + internal_function();
}
cpp 复制代码
// main.cpp - 使用模块
import math_utils;
#include <iostream>

int main() {
    Calculator calc;
    std::cout << "3 + 4 = " << add(3, 4) << std::endl;
    std::cout << "3 * 4 = " << calc.calculate(3, 4, '*') << std::endl;
    
    // internal_function() 不可访问 - 编译错误
    // auto x = internal_function();
    
    return 0;
}

优点: 显著加快编译速度(接口只编译一次)、强封装性(内部细节不暴露)、避免名称冲突、更清晰的代码组织。

概念 (Concepts)

目标: 为模板编程(尤其是泛型编程)提供强大的类型约束机制,使模板错误信息更清晰易懂,并支持基于概念的函数重载。

机制: 使用 concept 关键字定义对模板参数的要求(一组约束)。在模板参数列表或函数声明中使用 requires 子句或简写语法来应用概念。

代码示例:

cpp 复制代码
#include <concepts>
#include <iostream>
#include <vector>
#include <string>

// 1. 定义自定义概念
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

template<typename T>
concept Printable = requires(T t) {
    std::cout << t;  // 要求类型T可以输出到cout
};

template<typename T>
concept Container = requires(T t) {
    t.begin();       // 要求有begin()方法
    t.end();         // 要求有end()方法
    t.size();        // 要求有size()方法
};

// 2. 使用概念约束模板参数
// 方式1:requires子句
template<typename T>
requires Numeric<T>
T add(T a, T b) {
    return a + b;
}

// 方式2:简写语法
template<Numeric T>
T multiply(T a, T b) {
    return a * b;
}

// 方式3:概念作为模板参数
auto divide(Numeric auto a, Numeric auto b) {
    return a / b;
}

// 3. 复合概念约束
template<typename T>
requires Printable<T> && Container<T>
void print_container(const T& container) {
    std::cout << "Container contents: ";
    for (const auto& item : container) {
        std::cout << item << " ";
    }
    std::cout << std::endl;
}

// 4. 基于概念的函数重载
template<std::integral T>
void process(T value) {
    std::cout << "Processing integer: " << value << std::endl;
}

template<std::floating_point T>
void process(T value) {
    std::cout << "Processing float: " << value << std::endl;
}

template<typename T>
requires (!std::integral<T> && !std::floating_point<T>)
void process(T value) {
    std::cout << "Processing other type" << std::endl;
}

// 5. 更复杂的概念定义
template<typename T>
concept Comparable = requires(T a, T b) {
    { a < b } -> std::convertible_to<bool>;
    { a > b } -> std::convertible_to<bool>;
    { a == b } -> std::convertible_to<bool>;
};

template<Comparable T>
T max_value(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    // 数值运算
    std::cout << add(3, 4) << std::endl;           // OK: int是Numeric
    std::cout << multiply(3.14, 2.0) << std::endl; // OK: double是Numeric
    std::cout << divide(10, 3) << std::endl;       // OK: 自动推导
    
    // 容器打印
    std::vector<int> vec = {1, 2, 3, 4, 5};
    print_container(vec);  // OK: vector满足Printable和Container
    
    // 基于概念的重载
    process(42);      // 调用integral版本
    process(3.14);    // 调用floating_point版本
    process("hello"); // 调用其他类型版本
    
    // 比较操作
    std::cout << max_value(10, 20) << std::endl;
    std::cout << max_value(3.14, 2.71) << std::endl;
    
    // 编译错误示例(取消注释会导致编译错误)
    // add("hello", "world");  // 错误:string不是Numeric
    // multiply(vec, vec);      // 错误:vector不是Numeric
    
    return 0;
}

标准库提供的常用概念:

cpp 复制代码
#include <concepts>

// 核心语言概念
std::same_as<T, U>           // T和U是相同类型
std::derived_from<T, U>      // T派生自U
std::convertible_to<T, U>    // T可转换为U
std::common_reference_with<T, U>  // T和U有公共引用类型

// 算术概念
std::integral<T>             // T是整数类型
std::signed_integral<T>      // T是有符号整数
std::unsigned_integral<T>    // T是无符号整数
std::floating_point<T>       // T是浮点类型

// 比较概念
std::equality_comparable<T>   // T支持==比较
std::totally_ordered<T>      // T支持<, >, ==等全序比较

// 对象概念
std::movable<T>              // T可移动
std::copyable<T>             // T可复制
std::destructible<T>         // T可析构
std::constructible_from<T, Args...>  // T可从Args构造

优点: 大幅改善模板错误信息、在编译期精确检查模板参数是否满足要求、支持基于概念的重载和特化、使泛型代码意图更明确。

协程 (Coroutines)

目标: 提供语言级别的支持,简化异步编程(如网络I/O、生成器)和惰性求值,避免回调地狱或复杂的状态机管理。

机制: 引入新的关键字 co_await, co_yield, co_return。函数包含这些关键字即成为协程。编译器自动生成维持协程状态(帧)的代码。

核心类型: std::coroutine_handle, std::coroutine_traits, 以及相关的 promise 类型。

代码示例:

cpp 复制代码
#include <coroutine>
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>

// 1. 简单的生成器协程
template<typename T>
struct Generator {
    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 = value;
            return {};
        }
        
        void return_void() {}
        void unhandled_exception() {}
    };
    
    std::coroutine_handle<promise_type> h;
    
    explicit Generator(std::coroutine_handle<promise_type> handle) : h(handle) {}
    
    ~Generator() {
        if (h) h.destroy();
    }
    
    // 移动构造和赋值
    Generator(Generator&& other) noexcept : h(other.h) {
        other.h = nullptr;
    }
    
    Generator& operator=(Generator&& other) noexcept {
        if (this != &other) {
            if (h) h.destroy();
            h = other.h;
            other.h = nullptr;
        }
        return *this;
    }
    
    // 迭代器接口
    struct iterator {
        std::coroutine_handle<promise_type> h;
        
        iterator(std::coroutine_handle<promise_type> handle) : h(handle) {}
        
        iterator& operator++() {
            h.resume();
            if (h.done()) h = nullptr;
            return *this;
        }
        
        T operator*() const {
            return h.promise().current_value;
        }
        
        bool operator!=(const iterator& other) const {
            return h != other.h;
        }
    };
    
    iterator begin() {
        if (h) {
            h.resume();
            if (h.done()) return iterator{nullptr};
        }
        return iterator{h};
    }
    
    iterator end() {
        return iterator{nullptr};
    }
};

// 斐波那契数列生成器
Generator<int> fibonacci(int max_count) {
    int a = 0, b = 1;
    for (int i = 0; i < max_count; ++i) {
        co_yield a;
        auto temp = a + b;
        a = b;
        b = temp;
    }
}

// 范围生成器
Generator<int> range(int start, int end, int step = 1) {
    for (int i = start; i < end; i += step) {
        co_yield i;
    }
}

// 2. 简单的异步任务协程
struct Task {
    struct promise_type {
        Task get_return_object() {
            return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        
        void return_void() {}
        void unhandled_exception() {}
    };
    
    std::coroutine_handle<promise_type> h;
    
    explicit Task(std::coroutine_handle<promise_type> handle) : h(handle) {}
    
    ~Task() {
        if (h) h.destroy();
    }
    
    // 禁止复制,允许移动
    Task(const Task&) = delete;
    Task& operator=(const Task&) = delete;
    
    Task(Task&& other) noexcept : h(other.h) {
        other.h = nullptr;
    }
    
    Task& operator=(Task&& other) noexcept {
        if (this != &other) {
            if (h) h.destroy();
            h = other.h;
            other.h = nullptr;
        }
        return *this;
    }
};

// 简单的等待器
struct Awaiter {
    int delay_ms;
    
    bool await_ready() { return delay_ms <= 0; }
    
    void await_suspend(std::coroutine_handle<> handle) {
        std::thread([handle, this]() {
            std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
            handle.resume();
        }).detach();
    }
    
    void await_resume() {}
};

// 异步任务示例
Task async_task(const std::string& name, int delay) {
    std::cout << name << " started" << std::endl;
    
    co_await Awaiter{delay};
    
    std::cout << name << " finished after " << delay << "ms" << std::endl;
}

int main() {
    // 1. 生成器示例
    std::cout << "=== 生成器示例 ===" << std::endl;
    
    std::cout << "斐波那契数列前10项: ";
    for (auto value : fibonacci(10)) {
        std::cout << value << " ";
    }
    std::cout << std::endl;
    
    std::cout << "范围[0, 10), 步长2: ";
    for (auto value : range(0, 10, 2)) {
        std::cout << value << " ";
    }
    std::cout << std::endl;
    
    // 2. 异步任务示例
    std::cout << "\n=== 异步任务示例 ===" << std::endl;
    
    auto task1 = async_task("Task1", 1000);
    auto task2 = async_task("Task2", 500);
    auto task3 = async_task("Task3", 1500);
    
    // 等待所有任务完成(简化示例)
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));
    
    return 0;
}

协程的三个关键字:

  • co_await: 暂停协程执行,等待某个异步操作完成
  • co_yield: 暂停协程并返回一个值(用于生成器)
  • co_return: 结束协程执行并可选地返回一个值

协程的优势:

  1. 简化异步编程: 避免回调地狱,代码更直观
  2. 惰性求值: 生成器只在需要时计算下一个值
  3. 内存效率: 协程栈比线程栈小得多
  4. 可组合性: 协程可以轻松组合和链式调用

用途: 异步I/O、生成器、惰性序列、状态机等。

三向比较(飞船运算符)

目标: 简化用户自定义类型的比较运算符(==, !=, <, <=, >, >=)的定义。

机制: 定义一个 operator<=>(俗称飞船运算符)。它返回一个比较类别类型(std::strong_ordering, std::weak_ordering, std::partial_ordering),编译器可以根据这个运算符自动生成所有六个常规比较运算符(如果未显式定义它们)。

代码示例:

cpp 复制代码
#include <compare>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>

// 1. 基本的三向比较示例
struct Point {
    int x, y;
    
    // 自动生成所有比较运算符
    auto operator<=>(const Point& other) const = default;
    
    // 注意:C++20中,== 需要单独定义或使用 = default
    bool operator==(const Point& other) const = default;
};

// 2. 自定义三向比较逻辑
struct Person {
    std::string name;
    int age;
    
    // 先按年龄比较,年龄相同则按姓名比较
    std::strong_ordering operator<=>(const Person& other) const {
        if (auto cmp = age <=> other.age; cmp != 0) {
            return cmp;
        }
        return name <=> other.name;
    }
    
    bool operator==(const Person& other) const {
        return age == other.age && name == other.name;
    }
};

// 3. 不同比较类别的示例
struct Version {
    int 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& other) const = default;
};

// 4. 弱序比较示例
struct CaseInsensitiveString {
    std::string value;
    
    // 弱序:大小写不敏感比较
    std::weak_ordering operator<=>(const CaseInsensitiveString& other) const {
        auto to_lower = [](std::string s) {
            std::transform(s.begin(), s.end(), s.begin(), ::tolower);
            return s;
        };
        
        return to_lower(value) <=> to_lower(other.value);
    }
    
    bool operator==(const CaseInsensitiveString& other) const {
        return (*this <=> other) == 0;
    }
};

// 5. 偏序比较示例(浮点数)
struct FloatWrapper {
    double value;
    
    // 偏序:考虑NaN的情况
    std::partial_ordering operator<=>(const FloatWrapper& other) const {
        return value <=> other.value;
    }
    
    bool operator==(const FloatWrapper& other) const {
        return value == other.value;
    }
};

// 6. 混合类型比较
struct Temperature {
    double celsius;
    
    explicit Temperature(double c) : celsius(c) {}
    
    // 与double直接比较
    std::partial_ordering operator<=>(double other_celsius) const {
        return celsius <=> other_celsius;
    }
    
    bool operator==(double other_celsius) const {
        return celsius == other_celsius;
    }
    
    // 温度之间的比较
    auto operator<=>(const Temperature& other) const = default;
    bool operator==(const Temperature& other) const = default;
};

// 7. 复杂对象的比较
struct Student {
    std::string name;
    double gpa;
    int graduation_year;
    
    // 按GPA降序,然后按毕业年份升序,最后按姓名升序
    std::strong_ordering operator<=>(const Student& other) const {
        // GPA降序(注意顺序颠倒)
        if (auto cmp = other.gpa <=> gpa; cmp != 0) return cmp;
        
        // 毕业年份升序
        if (auto cmp = graduation_year <=> other.graduation_year; cmp != 0) return cmp;
        
        // 姓名升序
        return name <=> other.name;
    }
    
    bool operator==(const Student& other) const {
        return name == other.name && gpa == other.gpa && graduation_year == other.graduation_year;
    }
};

int main() {
    // 1. 基本点比较
    Point p1{1, 2};
    Point p2{3, 4};
    Point p3{1, 2};
    
    std::cout << "=== Point 比较 ===" << std::endl;
    std::cout << "p1 < p2: " << (p1 < p2) << std::endl;
    std::cout << "p1 == p3: " << (p1 == p3) << std::endl;
    std::cout << "p1 != p2: " << (p1 != p2) << std::endl;
    
    // 2. 人员比较
    Person alice{"Alice", 25};
    Person bob{"Bob", 30};
    Person charlie{"Charlie", 25};
    
    std::cout << "\n=== Person 比较 ===" << std::endl;
    std::cout << "alice < bob: " << (alice < bob) << std::endl;
    std::cout << "alice < charlie: " << (alice < charlie) << std::endl;
    
    // 3. 版本比较
    Version v1{1, 2, 3};
    Version v2{1, 2, 4};
    Version v3{2, 0, 0};
    
    std::cout << "\n=== Version 比较 ===" << std::endl;
    std::cout << "v1 < v2: " << (v1 < v2) << std::endl;
    std::cout << "v2 < v3: " << (v2 < v3) << std::endl;
    
    // 4. 大小写不敏感字符串
    CaseInsensitiveString s1{"Hello"};
    CaseInsensitiveString s2{"HELLO"};
    CaseInsensitiveString s3{"World"};
    
    std::cout << "\n=== 大小写不敏感字符串 ===" << std::endl;
    std::cout << "\"Hello\" == \"HELLO\": " << (s1 == s2) << std::endl;
    std::cout << "\"Hello\" < \"World\": " << (s1 < s3) << std::endl;
    
    // 5. 浮点数比较(包含NaN)
    FloatWrapper f1{3.14};
    FloatWrapper f2{2.71};
    FloatWrapper f3{std::numeric_limits<double>::quiet_NaN()};
    
    std::cout << "\n=== 浮点数比较 ===" << std::endl;
    std::cout << "3.14 > 2.71: " << (f1 > f2) << std::endl;
    std::cout << "NaN == NaN: " << (f3 == f3) << std::endl;  // false
    std::cout << "NaN < 3.14: " << (f3 < f1) << std::endl;   // false
    
    // 6. 温度比较
    Temperature t1{25.0};
    Temperature t2{30.0};
    
    std::cout << "\n=== 温度比较 ===" << std::endl;
    std::cout << "25°C < 30°C: " << (t1 < t2) << std::endl;
    std::cout << "25°C == 25.0: " << (t1 == 25.0) << std::endl;
    
    // 7. 学生排序
    std::vector<Student> students = {
        {"Alice", 3.8, 2024},
        {"Bob", 3.9, 2023},
        {"Charlie", 3.8, 2023},
        {"David", 3.9, 2024}
    };
    
    std::sort(students.begin(), students.end());
    
    std::cout << "\n=== 学生排序(按GPA降序,年份升序,姓名升序)===" << std::endl;
    for (const auto& student : students) {
        std::cout << student.name << " (GPA: " << student.gpa 
                  << ", 毕业年份: " << student.graduation_year << ")" << std::endl;
    }
    
    return 0;
}

比较类别说明:

  • std::strong_ordering: 强序,所有值都可比较,相等具有传递性

    • 返回值:less, equal, greater
    • 适用于:整数、字符串等
  • std::weak_ordering: 弱序,允许等价但不相等的值

    • 返回值:less, equivalent, greater
    • 适用于:大小写不敏感字符串比较
  • std::partial_ordering: 偏序,某些值可能无法比较

    • 返回值:less, equivalent, greater, unordered
    • 适用于:浮点数(包含NaN)

优点: 减少样板代码,确保比较运算符行为一致,支持隐式生成。

指定初始化 (Designated Initializers)

机制: 允许在初始化聚合类型(如结构体)时,显式指定要初始化的成员名称,使用 {.member1 = value1, .member2 = value2} 语法(类似于C语言)。

规则: 初始化器必须按照成员声明的顺序出现(C++20要求严格顺序,C99允许乱序),不能嵌套使用,不能与常规初始化混合。注意:C++20不支持数组的指定初始化。

代码示例:

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>

// 1. 基本结构体的指定初始化
struct Point {
    int x;
    int y;
    int z;
};

// 2. 包含不同类型成员的结构体
struct Person {
    std::string name;
    int age;
    double height;
    bool is_student;
};

// 3. 嵌套结构体
struct Address {
    std::string street;
    std::string city;
    int zip_code;
};

struct Employee {
    std::string name;
    int id;
    Address address;
    double salary;
};

// 4. 包含数组成员的结构体
struct Config {
    std::string app_name;
    int version[3];  // major, minor, patch
    bool debug_mode;
    double timeout;
};

// 5. 模板结构体
template<typename T>
struct Container {
    T value;
    std::string description;
    bool is_valid;
};

int main() {
    // 1. 基本指定初始化
    std::cout << "=== 基本指定初始化 ===" << std::endl;
    
    Point p1 = {.x = 10, .y = 20, .z = 30};
    Point p2 = {.x = 5, .y = 15};  // z 默认初始化为 0
    Point p3 = {.x = 1};           // y, z 默认初始化为 0
    
    std::cout << "p1: (" << p1.x << ", " << p1.y << ", " << p1.z << ")" << std::endl;
    std::cout << "p2: (" << p2.x << ", " << p2.y << ", " << p2.z << ")" << std::endl;
    std::cout << "p3: (" << p3.x << ", " << p3.y << ", " << p3.z << ")" << std::endl;
    
    // 2. 复杂类型的指定初始化
    std::cout << "\n=== 复杂类型指定初始化 ===" << std::endl;
    
    Person alice = {
        .name = "Alice Johnson",
        .age = 25,
        .height = 165.5,
        .is_student = true
    };
    
    Person bob = {
        .name = "Bob Smith",
        .age = 30
        // height 和 is_student 使用默认值
    };
    
    std::cout << alice.name << ": " << alice.age << " years, " 
              << alice.height << "cm, student: " << alice.is_student << std::endl;
    std::cout << bob.name << ": " << bob.age << " years, " 
              << bob.height << "cm, student: " << bob.is_student << std::endl;
    
    // 3. 嵌套结构体(注意:C++20不支持嵌套指定初始化)
    std::cout << "\n=== 嵌套结构体 ===" << std::endl;
    
    Employee emp1 = {
        .name = "John Doe",
        .id = 12345,
        .address = {"123 Main St", "New York", 10001},  // 常规初始化
        .salary = 75000.0
    };
    
    std::cout << emp1.name << " (ID: " << emp1.id << ")" << std::endl;
    std::cout << "Address: " << emp1.address.street << ", " 
              << emp1.address.city << " " << emp1.address.zip_code << std::endl;
    std::cout << "Salary: $" << emp1.salary << std::endl;
    
    // 4. 包含数组的结构体
    std::cout << "\n=== 包含数组的结构体 ===" << std::endl;
    
    Config app_config = {
        .app_name = "MyApp",
        .version = {2, 1, 0},  // 数组使用常规初始化
        .debug_mode = true,
        .timeout = 30.0
    };
    
    std::cout << "App: " << app_config.app_name << std::endl;
    std::cout << "Version: " << app_config.version[0] << "."
              << app_config.version[1] << "." << app_config.version[2] << std::endl;
    std::cout << "Debug: " << app_config.debug_mode << ", Timeout: " 
              << app_config.timeout << "s" << std::endl;
    
    // 5. 模板结构体的指定初始化
    std::cout << "\n=== 模板结构体 ===" << std::endl;
    
    Container<int> int_container = {
        .value = 42,
        .description = "Integer container",
        .is_valid = true
    };
    
    Container<std::string> str_container = {
        .value = "Hello, World!",
        .description = "String container"
        // is_valid 默认初始化为 false
    };
    
    std::cout << "Int container: " << int_container.value 
              << " (" << int_container.description << ")" << std::endl;
    std::cout << "String container: " << str_container.value 
              << " (" << str_container.description << ")" << std::endl;
    
    // 6. 部分初始化示例
    std::cout << "\n=== 部分初始化 ===" << std::endl;
    
    // 只初始化前几个成员
    Person partial_person = {
        .name = "Charlie Brown"
        // 其他成员使用默认值
    };
    
    std::cout << "Partial person: " << partial_person.name 
              << ", age: " << partial_person.age << std::endl;
    
    // 编译错误示例(取消注释会导致编译错误)
    /*
    // 错误:顺序不对
    Point wrong_order = {.y = 10, .x = 20};
    
    // 错误:混合指定初始化和常规初始化
    Point mixed = {10, .y = 20};
    
    // 错误:嵌套指定初始化
    Employee nested_designated = {
        .name = "Jane",
        .address = {.street = "456 Oak St"}  // 不支持
    };
    */
    
    return 0;
}

使用规则和限制:

  1. 顺序要求: 指定初始化器必须按照成员声明的顺序出现
  2. 不能混合: 不能在同一个初始化列表中混合指定初始化和常规初始化
  3. 不支持嵌套: 不能对嵌套的聚合类型使用指定初始化
  4. 不支持数组: C++20不支持数组的指定初始化(与C99不同)
  5. 只能用于聚合类型: 只能用于聚合类型(没有用户定义构造函数的类/结构体)

优点: 提高初始化代码的可读性和安全性,避免因成员顺序错误导致的初始化错误。

范围 for 循环中的初始化语句

机制: 允许在基于范围的 for 循环中初始化一个变量,语法为 for (init-statement; for-range-declaration : for-range-init)

代码示例:

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <map>

int main() {
    std::vector<std::string> words = {"hello", "world", "C++", "twenty"};
    std::map<std::string, int> word_count;
    
    // 1. 基本用法:在循环中声明计数器
    std::cout << "=== 带索引的范围循环 ===" << std::endl;
    for (int i = 0; const auto& word : words) {
        std::cout << "Index " << i << ": " << word << std::endl;
        ++i;
    }
    
    // 2. 初始化多个变量
    std::cout << "\n=== 初始化多个变量 ===" << std::endl;
    for (int count = 0, sum = 0; const auto& word : words) {
        sum += word.length();
        ++count;
        std::cout << "Word " << count << ": " << word 
                  << " (累计长度: " << sum << ")" << std::endl;
    }
    
    // 3. 使用初始化语句设置容器
    std::cout << "\n=== 使用初始化语句设置容器 ===" << std::endl;
    for (auto container = std::vector<int>{1, 2, 3, 4, 5}; const auto& value : container) {
        std::cout << value * value << " ";
    }
    std::cout << std::endl;
    
    // 4. 实际应用:统计和处理
    std::cout << "\n=== 统计处理示例 ===" << std::endl;
    for (size_t processed = 0; const auto& word : words) {
        word_count[word] = word.length();
        ++processed;
        std::cout << "已处理 " << processed << " 个单词" << std::endl;
    }
    
    // 5. 与传统for循环的对比
    std::cout << "\n=== 传统方式 vs 新方式 ===" << std::endl;
    
    // 传统方式(污染外层作用域)
    int traditional_index = 0;
    for (const auto& word : words) {
        if (traditional_index == 2) break;
        std::cout << "传统方式 - Index " << traditional_index << ": " << word << std::endl;
        ++traditional_index;
    }
    // traditional_index 仍然存在于外层作用域
    
    // 新方式(作用域限制在循环内)
    for (int modern_index = 0; const auto& word : words) {
        if (modern_index == 2) break;
        std::cout << "现代方式 - Index " << modern_index << ": " << word << std::endl;
        ++modern_index;
    }
    // modern_index 在这里不可访问
    
    return 0;
}

优点: 方便在循环作用域内声明循环计数器或其他临时变量,避免污染外层作用域。

[[likely]] 和 [[unlikely]] 属性

目标: 向编译器提供分支预测的提示,优化代码执行路径。

用法: 应用于 if, switch, 循环语句的标签或条件之前。

代码示例:

cpp 复制代码
#include <iostream>
#include <vector>
#include <random>
#include <chrono>

// 模拟一个可能很少失败的操作
bool risky_operation(int value) {
    return value % 100 != 0;  // 99% 的情况返回 true
}

// 处理数据的函数
void process_data(const std::vector<int>& data) {
    int success_count = 0;
    int failure_count = 0;
    
    for (const auto& value : data) {
        // 提示编译器这个分支很可能被执行
        if (risky_operation(value)) [[likely]] {
            success_count++;
            // 大部分情况下执行的代码
        } else [[unlikely]] {
            failure_count++;
            // 很少执行的错误处理代码
            std::cout << "处理失败: " << value << std::endl;
        }
    }
    
    std::cout << "成功: " << success_count << ", 失败: " << failure_count << std::endl;
}

// switch 语句中的使用
void handle_request(int request_type) {
    switch (request_type) {
        [[likely]] case 1:  // 最常见的请求类型
            std::cout << "处理常见请求" << std::endl;
            break;
            
        case 2:
            std::cout << "处理普通请求" << std::endl;
            break;
            
        [[unlikely]] case 99:  // 很少见的请求类型
            std::cout << "处理特殊请求" << std::endl;
            break;
            
        [[unlikely]] default:  // 错误情况
            std::cout << "未知请求类型" << std::endl;
            break;
    }
}

// 循环中的使用
void search_in_sorted_array(const std::vector<int>& sorted_data, int target) {
    for (const auto& value : sorted_data) {
        if (value == target) [[unlikely]] {
            // 找到目标的概率较低
            std::cout << "找到目标值: " << target << std::endl;
            return;
        }
        
        if (value > target) [[likely]] {
            // 在排序数组中,这种情况更可能发生
            std::cout << "目标值不存在" << std::endl;
            return;
        }
    }
}

// 性能关键代码示例
class Cache {
private:
    std::vector<std::pair<int, std::string>> cache_data;
    
public:
    std::string get(int key) {
        for (const auto& [k, v] : cache_data) {
            if (k == key) [[likely]] {
                // 缓存命中是期望的常见情况
                return v;
            }
        }
        
        // 缓存未命中,需要从其他地方加载
        [[unlikely]] {
            std::cout << "缓存未命中,加载数据..." << std::endl;
            return "default_value";
        }
    }
};

int main() {
    // 1. 基本的 likely/unlikely 使用
    std::cout << "=== 基本 likely/unlikely 示例 ===" << std::endl;
    
    std::vector<int> test_data;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(1, 1000);
    
    // 生成测试数据
    for (int i = 0; i < 10000; ++i) {
        test_data.push_back(dis(gen));
    }
    
    auto start = std::chrono::high_resolution_clock::now();
    process_data(test_data);
    auto end = std::chrono::high_resolution_clock::now();
    
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "处理时间: " << duration.count() << " 微秒" << std::endl;
    
    // 2. switch 语句示例
    std::cout << "\n=== Switch 语句示例 ===" << std::endl;
    std::vector<int> request_types = {1, 1, 1, 2, 1, 99, 1, 1, 2, 1};
    for (int type : request_types) {
        handle_request(type);
    }
    
    // 3. 搜索示例
    std::cout << "\n=== 搜索示例 ===" << std::endl;
    std::vector<int> sorted_array = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};
    search_in_sorted_array(sorted_array, 7);
    search_in_sorted_array(sorted_array, 8);
    
    // 4. 缓存示例
    std::cout << "\n=== 缓存示例 ===" << std::endl;
    Cache cache;
    std::cout << cache.get(1) << std::endl;
    std::cout << cache.get(2) << std::endl;
    
    return 0;
}

重要说明:

  1. 只是提示: 这些属性只是给编译器的提示,编译器可以选择忽略
  2. 性能优化: 主要用于性能关键的代码路径
  3. 分支预测: 帮助CPU的分支预测器做出更好的预测
  4. 不要滥用: 只在确实了解代码执行模式时使用

注意: 只是提示,编译器可以选择忽略。对性能关键代码有用。

constexpr 增强

C++20 大幅扩展了 constexpr 的能力,包括虚函数、动态转换、异常处理、动态内存分配等。

代码示例:

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <typeinfo>

// 1. constexpr 虚函数
struct Base {
    constexpr virtual ~Base() = default;
    constexpr virtual int getValue() const = 0;
    constexpr virtual const char* getType() const = 0;
};

struct Derived1 : public Base {
    int value;
    
    constexpr Derived1(int v) : value(v) {}
    
    constexpr int getValue() const override {
        return value;
    }
    
    constexpr const char* getType() const override {
        return "Derived1";
    }
};

struct Derived2 : public Base {
    int multiplier;
    
    constexpr Derived2(int m) : multiplier(m) {}
    
    constexpr int getValue() const override {
        return multiplier * 10;
    }
    
    constexpr const char* getType() const override {
        return "Derived2";
    }
};

// 使用 constexpr 虚函数
constexpr int processBase(const Base& base) {
    return base.getValue() * 2;
}

// 2. constexpr dynamic_cast
constexpr int safeCast(const Base& base) {
    if (const auto* d1 = dynamic_cast<const Derived1*>(&base)) {
        return d1->value + 100;
    } else if (const auto* d2 = dynamic_cast<const Derived2*>(&base)) {
        return d2->multiplier + 200;
    }
    return -1;
}

// 3. constexpr try-catch(主要用于SFINAE和模板元编程)
template<typename T>
constexpr bool canAdd(const T& a, const T& b) {
    try {
        auto result = a + b;
        return true;
    } catch (...) {
        return false;  // 在编译期,这实际上不会被执行
    }
}

// 4. constexpr std::vector 和 std::string
constexpr std::vector<int> createVector() {
    std::vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    return vec;
}

constexpr std::string createString() {
    std::string str = "Hello";
    str += ", ";
    str += "World!";
    return str;
}

constexpr int sumVector() {
    auto vec = createVector();
    int sum = 0;
    for (const auto& value : vec) {
        sum += value;
    }
    return sum;
}

// 5. consteval 立即函数
consteval int square(int n) {
    return n * n;
}

consteval int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

// consteval 函数必须在编译时求值
consteval const char* getCompileTimeString() {
    return "This is computed at compile time";
}

// 6. constinit 静态初始化
constinit static int global_value = 42;  // 保证静态初始化
constinit static std::string global_string = "initialized";  // 编译错误:string没有constexpr构造函数

// 正确的 constinit 用法
constinit static int computed_value = square(10);  // OK: square是consteval

// 7. 复杂的编译期计算
constexpr std::vector<int> generatePrimes(int max_num) {
    std::vector<int> primes;
    
    for (int num = 2; num <= max_num; ++num) {
        bool is_prime = true;
        for (int i = 2; i * i <= num; ++i) {
            if (num % i == 0) {
                is_prime = false;
                break;
            }
        }
        if (is_prime) {
            primes.push_back(num);
        }
    }
    
    return primes;
}

constexpr int countPrimesUpTo(int n) {
    auto primes = generatePrimes(n);
    return primes.size();
}

// 8. constexpr 算法
constexpr std::vector<int> sortVector(std::vector<int> vec) {
    // 简单的冒泡排序(在编译期执行)
    for (size_t i = 0; i < vec.size(); ++i) {
        for (size_t j = 0; j < vec.size() - 1 - i; ++j) {
            if (vec[j] > vec[j + 1]) {
                auto temp = vec[j];
                vec[j] = vec[j + 1];
                vec[j + 1] = temp;
            }
        }
    }
    return vec;
}

int main() {
    // 1. constexpr 虚函数测试
    std::cout << "=== constexpr 虚函数 ===" << std::endl;
    
    constexpr Derived1 d1(42);
    constexpr Derived2 d2(5);
    
    constexpr int result1 = processBase(d1);  // 编译期计算
    constexpr int result2 = processBase(d2);  // 编译期计算
    
    std::cout << "Derived1 处理结果: " << result1 << std::endl;
    std::cout << "Derived2 处理结果: " << result2 << std::endl;
    
    // 2. constexpr dynamic_cast
    std::cout << "\n=== constexpr dynamic_cast ===" << std::endl;
    
    constexpr int cast_result1 = safeCast(d1);
    constexpr int cast_result2 = safeCast(d2);
    
    std::cout << "Derived1 转换结果: " << cast_result1 << std::endl;
    std::cout << "Derived2 转换结果: " << cast_result2 << std::endl;
    
    // 3. constexpr 容器
    std::cout << "\n=== constexpr 容器 ===" << std::endl;
    
    constexpr auto compile_time_vector = createVector();
    constexpr auto compile_time_string = createString();
    constexpr int vector_sum = sumVector();
    
    std::cout << "编译期字符串: " << compile_time_string.c_str() << std::endl;
    std::cout << "编译期向量和: " << vector_sum << std::endl;
    
    // 4. consteval 函数
    std::cout << "\n=== consteval 函数 ===" << std::endl;
    
    constexpr int sq_5 = square(5);        // 编译期计算
    constexpr int fact_6 = factorial(6);   // 编译期计算
    
    std::cout << "5的平方: " << sq_5 << std::endl;
    std::cout << "6的阶乘: " << fact_6 << std::endl;
    std::cout << "编译期字符串: " << getCompileTimeString() << std::endl;
    
    // 5. 复杂编译期计算
    std::cout << "\n=== 复杂编译期计算 ===" << std::endl;
    
    constexpr int prime_count = countPrimesUpTo(100);
    constexpr auto primes = generatePrimes(30);
    
    std::cout << "100以内的质数个数: " << prime_count << std::endl;
    std::cout << "30以内的质数: ";
    for (const auto& prime : primes) {
        std::cout << prime << " ";
    }
    std::cout << std::endl;
    
    // 6. constexpr 排序
    std::cout << "\n=== constexpr 排序 ===" << std::endl;
    
    constexpr auto unsorted = std::vector<int>{5, 2, 8, 1, 9, 3};
    constexpr auto sorted = sortVector(unsorted);
    
    std::cout << "排序后: ";
    for (const auto& value : sorted) {
        std::cout << value << " ";
    }
    std::cout << std::endl;
    
    // 7. constinit 变量
    std::cout << "\n=== constinit 变量 ===" << std::endl;
    std::cout << "全局值: " << global_value << std::endl;
    std::cout << "计算值: " << computed_value << std::endl;
    
    // 编译错误示例(取消注释会导致编译错误)
    /*
    int runtime_value = 10;
    consteval int runtime_square = square(runtime_value);  // 错误:runtime_value不是编译期常量
    */
    
    return 0;
}

新增关键字说明:

  • consteval: 立即函数,强制在编译时求值

    cpp 复制代码
    consteval int compile_time_only(int x) {
        return x * x;
    }
  • constinit: 确保静态/线程局部变量的静态初始化

    cpp 复制代码
    constinit static int value = 42;  // 保证静态初始化

主要增强:

  1. 虚函数 : virtual 函数现在可以在 constexpr 上下文中使用
  2. dynamic_cast/typeid: 允许在常量表达式中使用动态转换
  3. try-catch : 允许在 constexpr 函数中使用异常处理
  4. 标准库容器 : std::vectorstd::string 等支持 constexpr
  5. 动态内存分配: 在编译期可以进行受限的动态内存分配

using enum 声明

机制: 将枚举类型的所有枚举项引入当前作用域,简化枚举的使用。

代码示例:

cpp 复制代码
#include <iostream>

enum class Color { Red, Green, Blue };
enum class Status { Active, Inactive, Pending };

void processColor() {
    using enum Color;  // 引入所有Color枚举项
    
    Color c1 = Red;    // 无需 Color::Red
    Color c2 = Green;
    Color c3 = Blue;
    
    std::cout << "Colors: " << static_cast<int>(c1) << ", " 
              << static_cast<int>(c2) << ", " << static_cast<int>(c3) << std::endl;
}

void processStatus() {
    using Status::Active;  // 只引入单个枚举项
    
    Status s1 = Active;           // OK
    Status s2 = Status::Pending;  // 仍需前缀
}

int main() {
    processColor();
    processStatus();
    return 0;
}

其他语言特性

聚合体的圆括号初始化:

cpp 复制代码
struct Base { int x; };
struct Derived : Base { int y; };

Derived d1{Base{1}, 2};  // C++17: 花括号
Derived d2(Base{1}, 2);  // C++20: 圆括号

位域默认初始化:

cpp 复制代码
struct Flags {
    unsigned int flag1 : 1 = 1;  // 默认为1
    unsigned int flag2 : 2 = 0;  // 默认为0
    unsigned int flag3 : 3;      // 无默认值
};

VA_OPT 宏:

cpp 复制代码
#define LOG(msg, ...) printf(msg __VA_OPT__(,) __VA_ARGS__)

LOG("Hello");           // printf("Hello")
LOG("Value: %d", 42);   // printf("Value: %d", 42)

Lambda 捕获增强:

cpp 复制代码
class MyClass {
public:
    void method() {
        int x = 10;
        
        // C++20 明确允许
        auto lambda1 = [=, this]() { return x + member_var; };
        
        // 推荐的明确写法
        auto lambda2 = [this, x]() { return x + member_var; };
    }
    
private:
    int member_var = 5;
};

📚 二、标准库特性

1. 范围库 (Ranges Library)

目标: 提供操作元素序列的现代化、可组合、惰性求值的算法和视图。

代码示例:

cpp 复制代码
#include <ranges>
#include <vector>
#include <iostream>
#include <algorithm>
#include <string>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 链式操作:过滤偶数 -> 平方 -> 取前3个
    auto result = numbers 
        | std::views::filter([](int n) { return n % 2 == 0; })
        | std::views::transform([](int n) { return n * n; })
        | std::views::take(3);
    
    std::cout << "偶数平方前3个: ";
    for (int value : result) {
        std::cout << value << " ";  // 输出: 4 16 36
    }
    std::cout << std::endl;
    
    // 生成无限序列
    auto infinite_odds = std::views::iota(1)
        | std::views::filter([](int n) { return n % 2 == 1; })
        | std::views::take(5);
    
    std::cout << "前5个奇数: ";
    for (int value : infinite_odds) {
        std::cout << value << " ";  // 输出: 1 3 5 7 9
    }
    std::cout << std::endl;
    
    // 范围算法
    std::vector<std::string> words = {"hello", "world", "cpp", "ranges"};
    std::ranges::sort(words);  // 直接对范围排序
    
    auto it = std::ranges::find(words, "cpp");
    if (it != words.end()) {
        std::cout << "找到单词: " << *it << std::endl;
    }
    
    // 复杂的范围操作
    std::vector<std::vector<int>> matrix = {{1, 2}, {3, 4}, {5, 6}};
    auto flattened = matrix | std::views::join;
    
    std::cout << "展平矩阵: ";
    for (int value : flattened) {
        std::cout << value << " ";  // 输出: 1 2 3 4 5 6
    }
    std::cout << std::endl;
    
    return 0;
}

核心组件:

  • 范围概念: std::ranges::range, std::ranges::view, std::ranges::sized_range
  • 视图 (Views): filter, transform, take, join, iota
  • 范围算法: std::ranges::sort, std::ranges::find, std::ranges::for_each

优点: 代码简洁、惰性求值、可组合性强、支持无限序列

2. std::format 格式化库

目标: 提供现代化、类型安全、高性能的文本格式化机制。

代码示例:

cpp 复制代码
#include <format>
#include <iostream>
#include <string>
#include <vector>

struct Point {
    double x, y;
};

// 为自定义类型提供格式化支持
template<>
struct std::formatter<Point> {
    constexpr auto parse(format_parse_context& ctx) {
        return ctx.begin();
    }
    
    template<typename FormatContext>
    auto format(const Point& p, FormatContext& ctx) {
        return std::format_to(ctx.out(), "({:.2f}, {:.2f})", p.x, p.y);
    }
};

int main() {
    // 基本格式化
    std::string name = "Alice";
    int age = 30;
    double salary = 75000.50;
    
    std::string basic = std::format("姓名: {}, 年龄: {}, 薪水: {:.2f}", name, age, salary);
    std::cout << basic << std::endl;
    
    // 位置参数
    std::string positioned = std::format("{1} 今年 {0} 岁", age, name);
    std::cout << positioned << std::endl;
    
    // 格式说明符
    int number = 42;
    std::cout << std::format("十进制: {}, 十六进制: {:x}, 八进制: {:o}, 二进制: {:b}\n", 
                            number, number, number, number);
    
    // 对齐和宽度
    std::cout << std::format("左对齐: '{:<10}', 右对齐: '{:>10}', 居中: '{:^10}'\n", 
                            "hello", "hello", "hello");
    
    // 浮点数精度
    double pi = 3.14159265359;
    std::cout << std::format("π = {:.2f}, π = {:.6f}, π = {:e}\n", pi, pi, pi);
    
    // 自定义类型
    Point p{3.14, 2.71};
    std::cout << std::format("点坐标: {}\n", p);
    
    // 容器格式化(需要自定义formatter或使用范围)
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    std::string result = "数字: ";
    for (size_t i = 0; i < numbers.size(); ++i) {
        result += std::format("{}{}", numbers[i], (i < numbers.size() - 1) ? ", " : "");
    }
    std::cout << result << std::endl;
    
    // 性能优化:预计算大小
    auto size = std::formatted_size("Hello {}", "World");
    std::cout << std::format("格式化字符串大小: {} 字节\n", size);
    
    // 直接输出到迭代器
    std::string buffer;
    buffer.reserve(100);
    std::format_to(std::back_inserter(buffer), "直接输出: {} + {} = {}", 10, 20, 30);
    std::cout << buffer << std::endl;
    
    return 0;
}

主要特性:

  • 类型安全: 编译时检查参数类型
  • 丰富格式: 支持对齐、宽度、精度、进制等
  • 可扩展: 支持自定义类型的格式化
  • 高性能: 通常优于 printfiostreams
  • 相关函数: std::formatted_size, std::format_to

3. std::span

目标: 提供对连续内存序列的非占有视图,安全地传递数组或容器的一部分。

代码示例:

cpp 复制代码
#include <span>
#include <vector>
#include <array>
#include <iostream>

// 函数接受span参数,可以处理各种容器类型
void print_elements(std::span<const int> data) {
    std::cout << "元素个数: " << data.size() << ", 内容: ";
    for (const auto& elem : data) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

void modify_elements(std::span<int> data) {
    for (auto& elem : data) {
        elem *= 2;  // 将每个元素乘以2
    }
}

int main() {
    // 不同类型的容器
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::array<int, 4> arr = {10, 20, 30, 40};
    int c_array[] = {100, 200, 300};
    
    // span可以从各种容器创建
    std::span<int> span_vec(vec);
    std::span<int> span_arr(arr);
    std::span<int> span_c_array(c_array);
    
    std::cout << "原始数据:\n";
    print_elements(span_vec);
    print_elements(span_arr);
    print_elements(span_c_array);
    
    // 修改数据
    modify_elements(span_vec);
    modify_elements(span_arr.subspan(1, 2));  // 只修改部分元素
    
    std::cout << "\n修改后:\n";
    print_elements(span_vec);
    print_elements(span_arr);
    
    // 子视图操作
    std::span<int> sub_span = span_vec.subspan(1, 3);  // 从索引1开始,取3个元素
    std::cout << "\n子视图: ";
    print_elements(sub_span);
    
    // 固定大小的span
    std::span<int, 3> fixed_span = span_c_array;  // 编译时大小检查
    std::cout << "固定大小span: ";
    print_elements(fixed_span);
    
    // span的其他操作
    std::cout << "\nspan操作演示:\n";
    std::cout << "第一个元素: " << span_vec.front() << std::endl;
    std::cout << "最后一个元素: " << span_vec.back() << std::endl;
    std::cout << "数据指针: " << span_vec.data() << std::endl;
    std::cout << "是否为空: " << span_vec.empty() << std::endl;
    
    return 0;
}

主要特性:

  • 轻量级: 通常只包含指针和大小
  • 类型安全: 编译时类型检查
  • 统一接口: 可以处理数组、vector、array等
  • 子视图: 支持 subspan() 操作

4. std::jthread (协作式中断线程)

目标: 提供比 std::thread 更安全、支持协作式中断的线程类型。

代码示例:

cpp 复制代码
#include <thread>
#include <iostream>
#include <chrono>
#include <stop_token>

void worker_function(std::stop_token stoken, int id) {
    int count = 0;
    while (!stoken.stop_requested()) {
        std::cout << "线程 " << id << " 工作中... (" << ++count << ")" << std::endl;
        
        // 模拟工作,同时检查停止请求
        if (std::this_thread::sleep_for(std::chrono::milliseconds(500)); 
            stoken.stop_requested()) {
            std::cout << "线程 " << id << " 收到停止请求" << std::endl;
            break;
        }
    }
    std::cout << "线程 " << id << " 结束" << std::endl;
}

void callback_example(std::stop_token stoken) {
    // 注册停止回调
    std::stop_callback callback(stoken, []() {
        std::cout << "停止回调被触发!" << std::endl;
    });
    
    int count = 0;
    while (!stoken.stop_requested() && count < 10) {
        std::cout << "回调示例线程运行: " << ++count << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
    }
}

int main() {
    std::cout << "=== std::jthread 示例 ===\n";
    
    // 创建jthread,自动管理生命周期
    std::jthread worker1(worker_function, 1);
    std::jthread worker2(worker_function, 2);
    std::jthread callback_thread(callback_example);
    
    // 让线程运行一段时间
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    std::cout << "\n请求停止所有线程...\n";
    
    // 请求停止
    worker1.request_stop();
    worker2.request_stop();
    callback_thread.request_stop();
    
    // jthread析构时会自动join,无需手动调用
    std::cout << "等待线程结束...\n";
    
    // 也可以手动join
    if (worker1.joinable()) worker1.join();
    if (worker2.joinable()) worker2.join();
    if (callback_thread.joinable()) callback_thread.join();
    
    std::cout << "所有线程已结束\n";
    
    // 演示stop_source和stop_token的独立使用
    std::cout << "\n=== 独立使用 stop_token ===\n";
    
    std::stop_source source;
    std::stop_token token = source.get_token();
    
    std::jthread independent_thread([token](std::stop_token) {
        while (!token.stop_requested()) {
            std::cout << "独立线程运行中..." << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(400));
        }
        std::cout << "独立线程结束" << std::endl;
    });
    
    std::this_thread::sleep_for(std::chrono::seconds(1));
    source.request_stop();  // 通过source请求停止
    
    return 0;
}

主要优势:

  • 自动join: 析构时自动调用 join()
  • 协作式中断: 通过 std::stop_token 安全停止
  • RAII管理: 更安全的资源管理
  • 向后兼容: 可以像 std::thread 一样使用
  1. std::stop_token / std::stop_source / std::stop_callback:

    • 目标:std::jthread 提供协作式中断机制,也可独立使用。
    • 机制:
      • std::stop_source: 产生中断请求 (request_stop()),拥有一个共享的停止状态。
      • std::stop_token: 从 stop_source 获取,用于查询是否发生了中断请求 (stop_requested()) 或注册回调。
      • std::stop_callback: 向 stop_token 注册一个回调函数,在中断请求发生时(或如果已发生则立即)调用该回调。
    • 优点: 标准化的、灵活的线程协作取消机制。
  2. 原子智能指针 (std::atomic<, std::atomic>):

    • 目标: 提供对 std::shared_ptrstd::weak_ptr 的原子操作支持。
    • 机制: 类模板 std::atomicstd::atomic。提供 load, store, exchange, compare_exchange_weak/strong 等原子操作,以及 std::atomic_is_lock_free 检查。
    • 优点: 无需手动加锁即可在多线程环境中安全地读写 shared_ptr/weak_ptr 本身(注意:不保护其指向的对象)。
  3. std::latchstd::barrier:

    • 目标: 提供线程同步原语,协调多个线程在某个点上的汇合。
    • std::latch: 一次性屏障。初始化一个计数值,线程调用 count_down() 减少计数或 wait() 阻塞直到计数减到零。计数到零后,所有等待的线程解除阻塞,后续的 count_down 无操作,wait 立即返回。
    • std::barrier: 可重复使用的屏障。初始化一个计数值和一个(可选的)完成阶段函数。线程调用 arrive_and_wait() 减少计数并阻塞,直到所有线程都到达。当计数减到零时,调用完成函数,然后重置计数并解除所有等待线程的阻塞,进入下一阶段。
    • 优点: 简化常见的多线程同步模式(如等待所有工作线程完成初始化、分阶段处理)。
  4. std::counting_semaphore / std::binary_semaphore:

    • 目标: 提供计数信号量(资源计数器)和二元信号量(互斥锁)。
    • 机制: std::counting_semaphore<Max> (其中 Max 是最大计数值) 和 std::binary_semaphore (是 std::counting_semaphore<1> 的别名)。核心操作:acquire() (P 操作,获取资源,可能阻塞), try_acquire() (非阻塞尝试), try_acquire_for()/try_acquire_until() (带超时尝试), release() (V 操作,释放资源)。
    • 优点: 标准化的信号量实现,用于控制对共享资源的并发访问数量。
  5. std::source_location:

    • 目标: 在代码中获取当前的源文件位置(文件名、行号、函数名、列号),替代 __FILE__, __LINE__ 等宏,且可作为函数默认参数。
    • 机制:std::source_location。使用 std::source_location::current() 静态方法获取调用点的位置信息。成员函数:file_name(), line(), column(), function_name()
    • 优点: 类型安全、可作为默认参数(宏不行)、更符合现代C++风格。
  6. std::chrono 日历和时区扩展:

    • 目标: 提供完整的日历日期和时区处理能力。
    • 机制:
      • 日历类型: std::chrono::year, month, day, weekday, month_day, year_month, year_month_day, year_month_weekday 等。支持丰富的算术和比较操作。
      • 时区支持: std::chrono::time_zone, std::chrono::zoned_time (表示特定时区的绝对时间点), std::chrono::tzdb (管理时区数据库)。支持在时区之间转换时间点、获取本地时间、处理夏令时等。
      • 格式化和解析:std::format / std::parse 集成。
    • 优点: 标准化的、强大的日期时间处理库,替代第三方库。
  7. constexpr 算法和容器:

    • 机制: 许多标准算法(如 std::find, std::sort - 在满足 constexpr 要求时)和容器(std::vector, std::string, std::array, std::pair, std::tuple 等)在 C++20 中被声明为可在 constexpr 上下文中使用(通常要求元素类型是字面类型,操作在编译期可求值)。
    • 优点: 支持在编译期进行更复杂的计算和数据结构操作。
  8. std::erase / std::erase_if (非成员函数):

    • 机制: 为序列容器 (vector, deque, list, forward_list) 和关联容器 (map, set, unordered_map, unordered_set) 提供统一的非成员函数,用于删除满足特定条件或等于特定值的所有元素。
    • 优点: 提供更一致、更简洁的擦除语法(相比容器的 erase 成员函数迭代器循环)。
  9. std::bit_cast:

    • 目标: 安全、低开销地重新解释一种对象类型为另一种具有相同大小的对象类型(类似 reinterpret_cast,但保证 constexpr 且避免未定义行为)。
    • 机制: template To bit_cast(const From& from) noexcept;。要求 sizeof(To) == sizeof(From) 且两者都是 TriviallyCopyable 类型。
    • 优点: 安全地进行位级别的类型转换(如浮点数到整数表示),避免 memcpyreinterpret_cast 的陷阱。

🚀 总结

C++20 是一次巨大的飞跃,引入了改变游戏规则的核心特性(模块、概念、协程 )以及大量提升开发效率和代码质量的库特性(范围、format、span、jthread、同步原语、日历时区)。它显著改善了泛型编程(概念)、编译构建(模块)、异步编程(协程)、并发编程(jthread, 信号量, latch/barrier)、文本处理(format)和日期时间处理(chrono扩展)等方面的体验。掌握 C++20 是编写现代、高效、安全 C++ 代码的关键。后续版本(C++23, C++26)在 C++20 的基础上继续完善和扩展这些特性。

相关推荐
pay4fun34 分钟前
2048-控制台版本
c++·学习
hjjdebug2 小时前
ffplay6 播放器关键技术点分析 1/2
c++·ffmpeg·音视频
Azxcc03 小时前
C++异步编程入门
开发语言·c++
吐泡泡_3 小时前
C++(STL源码刨析/vector)
c++
你的冰西瓜3 小时前
C++排序算法全解析(加强版)
c++·算法·排序算法
特立独行的猫a3 小时前
11款常用C++在线编译与运行平台推荐与对比
java·开发语言·c++
笑鸿的学习笔记4 小时前
qt-C++笔记之setCentralWidget的使用
c++·笔记·qt
苏克贝塔5 小时前
Qt 图形视图框架3-事件处理与传播
c++·qt
轩情吖5 小时前
Qt的信号与槽(二)
数据库·c++·qt·信号·connect·信号槽·
胖大和尚5 小时前
C++项目学习计划
开发语言·c++·学习