C++20 实战心得:现代 C++ 真正成熟的一代

C++20 实战心得:现代 C++ 真正成熟的一代 C++11 是革命,C++17 是打磨,C++20 是让 C++ 终于像一门「现代语言」。 写在前面 如果说 C++17 的升级是务实的,那 C++20 就是一次结构性的飞跃。协程、Concepts、Ranges、Modules——每一个都是重量级特性。但老实说,截至 2026 年,并非所有特性都已经在生产环境中稳定好用。 这篇文章从我在游戏服务器和 Hical 框架开发中的实际使用出发,聊聊哪些 C++20 特性已经值得用、哪些还需要等等。 一、Concepts —— 模板错误信息终于能看懂了 1.1 C++20 之前的模板报错 先感受一下 C++17 时代的"恐怖": 1 2 std::list<int> lst; std::sort(lst.begin(), lst.end()); GCC 会喷出几十行模板展开错误,核心意思是 std::list::iterator 不是随机访问迭代器——但你得从一堆 __normal_iterator、__gnu_cxx 嵌套模板中自己悟出来。 1.2 Concepts:把约束说人话 1 2 3 4 5 6 7 8 9 template <std::random_access_iterator Iter> void mySort(Iter first, Iter last) { // ... } std::list<int> lst; mySort(lst.begin(), lst.end()); // 错误信息:约束 'random_access_iterator' 不满足 // 一行,清清楚楚 Concepts 的本质:给模板参数加上编译期的「类型契约」。SFINAE 能做的它都能做,但写法是人能读懂的。 ...

April 20, 2026 · 8 min · 1683 words

从零构建现代C++ Web服务器(一):设计理念与架构总览

从零构建现代C++ Web服务器(一):设计理念与架构总览 系列导航:第一篇:设计理念(本文) | 第二篇:协程与内存池 | 第三篇:路由、中间件与SSL | 第四篇:实战与性能 | 第五篇:Cookie、Session与文件服务 | 第六篇:数据库中间件 前置知识 熟悉 C++17、C++20 基础语法(模板、智能指针、lambda、协程、Concepts) 了解 TCP/IP 和 HTTP 协议基本概念 对异步编程模型有初步认知 目录 1. 为什么在 2026 年用 C++ 写 Web 框架? 2. 现有方案分析 3. hical 的设计目标 4. 两层架构设计 5. C++20 Concepts 做后端抽象 6. 线程模型:1 Thread : 1 io_context 7. 全文总结 1. 为什么在 2026 年用 C++ 写 Web 框架? 当大多数团队选择 Go、Rust 或 Node.js 构建 Web 服务时,用 C++ 写 Web 框架似乎是"逆潮流而行"。但事实是,在特定场景下 C++ 仍然不可替代: 极致性能需求:游戏服务器、实时通信、高频交易等场景对延迟敏感到微秒级别 与现有 C++ 生态集成:当你的业务逻辑、数据处理库本身就是 C++ 时,跨语言调用引入的开销和复杂度不可忽视 内存可控:C++ 没有 GC 暂停,配合内存池可以实现完全可预测的内存行为 更重要的是,C++20/26 带来了一系列改变游戏规则的特性: ...

April 12, 2026 · 9 min · 1789 words

用 C++20 Concepts 设计可替换的网络后端:从 Boost.Asio 到未来的 io_uring

用 C++20 Concepts 设计可替换的网络后端:从 Boost.Asio 到未来的 io_uring 本文以 Hical 框架为例,展示如何用 C++20 Concepts 约束网络后端接口,实现编译期类型安全的后端抽象。 问题:网络后端绑定的困境 大多数 C++ 网络框架和底层网络库深度绑定。Drogon 绑定 Trantor,muduo 绑定自研的 EventLoop。一旦想换后端(比如从 epoll 切到 io_uring),基本等于重写。 原因是传统的抽象手段——虚函数继承——有两个问题: 运行时开销:每次调用都经过 vtable 接口松散:基类定义了接口,但"你的实现是否真的完整?“只能在链接期或运行时才知道 Concepts:编译期的接口约束 C++20 Concepts 提供了一种具名约束机制——在编译期验证类型是否满足一组要求: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 template <typename T> concept EventLoopLike = requires(T loop, std::function<void()> func, double delay) { { loop.run() } -> std::same_as<void>; { loop.stop() } -> std::same_as<void>; { loop.isRunning() } -> std::convertible_to<bool>; { loop.post(func) } -> std::same_as<void>; { loop.dispatch(func) } -> std::same_as<void>; { loop.runAfter(delay, func) } -> std::convertible_to<uint64_t>; { loop.runEvery(delay, func) } -> std::convertible_to<uint64_t>; { loop.cancelTimer(uint64_t{}) } -> std::same_as<void>; { loop.isInLoopThread() } -> std::convertible_to<bool>; { loop.index() } -> std::convertible_to<size_t>; { loop.allocator() } -> std::same_as<std::pmr::polymorphic_allocator<std::byte>>; }; 如果某个类型缺少 run() 方法或返回类型不对,编译器立即报错,而不是在链接时给出晦涩的"未定义引用”。 ...

April 12, 2026 · 3 min · 580 words