C++23 新特性:协程与 constexpr 改进

C++23 是 C++ 标准的又一次重要更新,既在语言层面做了细致的优化,又在标准库中引入了大量实用的新特性。本文聚焦两个最受关注的改进:协程(Coroutines)以及 constexpr 的进一步扩展。通过实例代码,帮助读者快速上手,并在项目中灵活运用。

一、协程(Coroutines)快速入门

1.1 协程的核心概念

协程是“轻量级协作式线程”,允许函数在执行过程中暂停并恢复,从而实现异步编程、生成器等功能。C++23 通过引入 std::suspend_alwaysstd::suspend_neverstd::yield_value 等工具,让协程更易于使用。

1.2 基础协程例子:整数序列生成器

#include <iostream>
#include <coroutine>
#include <optional>

template<typename T>
struct Generator {
    struct promise_type;
    using handle_type = std::coroutine_handle <promise_type>;

    struct promise_type {
        T value_;
        std::optional <T> current_;

        auto get_return_object() { return Generator{handle_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_ = std::move(value);
            return {};
        }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };

    handle_type coro_;
    explicit Generator(handle_type h) : coro_(h) {}
    ~Generator() { if (coro_) coro_.destroy(); }

    bool next() {
        if (!coro_.done()) coro_.resume();
        return !coro_.done();
    }

    T value() const { return *coro_.promise().current_; }
};

Generator <int> count_up_to(int limit) {
    for (int i = 1; i <= limit; ++i)
        co_yield i;
}

int main() {
    auto gen = count_up_to(5);
    while (gen.next()) {
        std::cout << gen.value() << ' ';
    }
    // 输出: 1 2 3 4 5
}

此示例演示了如何通过 co_yield 实现生成器,C++23 中 std::suspend_always 让协程的挂起点更直观。

1.3 协程在异步 IO 中的应用

借助 std::asyncstd::future,结合协程可以写出更直观的异步代码:

#include <iostream>
#include <coroutine>
#include <future>

std::future <int> async_add(int a, int b) {
    co_return a + b;
}

int main() {
    auto fut = async_add(3, 4);
    std::cout << "Result: " << fut.get() << '\n';
}

协程让异步编程的逻辑结构更接近同步代码,降低了回调地狱。

二、constexpr 的进一步扩展

2.1 constexpr 递归模板更安全

C++23 对 constexpr 的递归模板调用做了更严格的检查,减少了编译期错误:

constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}
static_assert(factorial(5) == 120);

编译器会在编译期计算结果,若出现无效递归会给出清晰错误提示。

2.2 constexpr 动态内存

C++23 引入 std::pmr::monotonic_buffer_resourceconstexpr 环境下可用,允许在编译期进行内存分配:

#include <memory_resource>
#include <array>

constexpr std::pmr::monotonic_buffer_resource pool;
constexpr std::pmr::polymorphic_allocator <int> alloc(&pool);

constexpr std::vector<int, std::pmr::polymorphic_allocator<int>> vec{alloc, {1,2,3,4,5}};
static_assert(vec.size() == 5);

这为编译期生成复杂数据结构提供了可能。

2.3 constexpr 与多线程

C++23 允许在 constexpr 环境下使用 std::thread 的构造函数(但不执行)。这为在编译期验证线程安全性提供了工具:

constexpr void check_thread_safety() {
    std::thread t([]{ /* 线程工作 */ });
    // 编译期检查构造是否合法
}

三、实践建议

  1. 模块化:在 C++23 中结合协程和模块化,减少编译时间。将协程实现放在模块中,避免每次编译都要重新编译协程代码。
  2. constexpr 静态数据:利用 constexpr 动态内存生成编译期常量表,适用于配置系统或生成固定算法表。
  3. 协程与异步 IO:在网络或文件 IO 场景,使用 co_await 与异步函数组合,写出更易读的非阻塞代码。
  4. 单元测试:使用 static_assert 验证 constexpr 结果,保证逻辑正确性。

结语

C++23 的协程和 constexpr 的改进,进一步提升了语言的表达力和性能。掌握这些特性后,开发者可以在项目中实现更高效、更易维护的代码。希望本文的示例和建议能帮助你快速上手,并在实际项目中获得收益。祝编码愉快!

发表评论