C++26 前瞻心得:下一代 C++ 最值得期待的特性

C++26 前瞻心得:下一代 C++ 最值得期待的特性 C++11 让 C++ 进入现代,C++20 让 C++ 追上时代,C++26 要让 C++ 重新定义「零开销抽象」的边界。 写在前面 C++26 标准预计 2026 年底正式发布。截至本文写作时(2026 年 5 月),核心特性已基本锁定,部分编译器开始提供实验性支持。 这篇文章不追求完整列举所有提案,只聊我认为对实际项目冲击最大的特性——尤其从游戏服务器和 Hical 框架开发的角度。这是 C++17 心得 和 C++20 心得 的续篇。 声明:部分特性的最终语法可能随标准定稿而调整,代码示例基于当前最新提案。 一、静态反射(Static Reflection)—— C++ 的 Game Changer 1.1 为什么反射是最重要的 C++26 特性 在 Java、C#、Go 中习以为常的操作——遍历结构体字段、获取类名、自动序列化——在 C++ 中一直只能靠宏或代码生成。C++26 的反射(P2996)让编译器在编译期暴露类型的元信息: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <meta> struct Player { uint64_t id; std::string name; int level; int64_t gold; }; // 编译期遍历所有成员 template <typename T> void printFields(const T& obj) { template for (constexpr auto member : std::meta::members_of(^T)) { if constexpr (std::meta::is_nonstatic_data_member(member)) { std::println(" {}: {}", std::meta::name_of(member), obj.[:member:]); } } } Player p{1001, "Hical", 85, 999999}; printFields(p); // 输出: // id: 1001 // name: Hical // level: 85 // gold: 999999 零运行时开销,不需要宏,不需要代码生成工具,编译器原生支持。 ...

April 21, 2026 · 8 min · 1554 words

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++17 实战心得:那些真正改变我写代码方式的特性

C++17 实战心得:那些真正改变我写代码方式的特性 从游戏服务器开发的视角出发,不求面面俱到,只聊那些真正让我「回不去了」的 C++17 特性。 写在前面 C++17 的特性列表很长,但实际工作中高频使用的并不多。这篇文章只聊我在游戏服务器开发中真正用上了、且明显感到提升的特性,按「爽度」排序。 一、结构化绑定(Structured Bindings) 1.1 告别 .first / .second C++17 之前,遍历 std::map 是这样的: 1 2 3 4 5 for (auto it = playerMap.begin(); it != playerMap.end(); ++it) { auto playerId = it->first; auto& player = it->second; // ... } C++17 之后: 1 2 3 for (auto& [playerId, player] : playerMap) { LOG_DEBUG << "玩家 " << playerId << " 等级: " << player.level; } 一行搞定,变量名直接表达语义,可读性提升巨大。 1.2 配合 insert / emplace 的返回值 1 2 3 4 auto [iter, success] = onlinePlayers.emplace(playerId, std::move(session)); if (!success) { LOG_WARN << "玩家 " << playerId << " 重复登录"; } 比起 result.second 去判断是否插入成功,success 的语义一目了然。 1.3 多返回值函数 1 2 // 解析网络包头:返回包类型和包体长度 auto [msgType, bodyLen] = parsePacketHeader(buffer); 不用再纠结「该用 std::pair 还是定义一个临时结构体」的问题了。当然,如果返回值超过 3 个,还是老老实实定义结构体。 ...

April 19, 2026 · 5 min · 1014 words

深入学习 C++26 静态反射(Static Reflection)

深入学习 C++26 静态反射(Static Reflection) 提案:P2996R9(Reflection for C++26) 头文件:<meta> 命名空间:std::meta 编译器支持:Clang(P2996 实验分支)、EDG(部分)/ GCC 和 MSVC 计划中 注意:C++26 标准预计 2026 年底定稿,本文语法基于当前最新提案,最终可能有微调 一、为什么需要静态反射? 1.1 C++ 元编程的历史痛点 C++ 一直以"零开销抽象"著称,但在类型自省这件事上,四十年来只能靠旁门左道: 方案 A:宏暴力展开 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // 用宏定义可序列化结构体 #define DEFINE_FIELDS(TYPE, ...) \ static constexpr auto fields() { \ return std::make_tuple(__VA_ARGS__); \ } struct Player { uint64_t id; std::string name; int level; int64_t gold; DEFINE_FIELDS(Player, FIELD(id), FIELD(name), FIELD(level), FIELD(gold) // 手动列举每个字段 ) }; 方案 B:代码生成工具(protobuf / flatbuffers / 自研工具) ...

April 9, 2026 · 27 min · 5613 words

深入学习 C++20 协程(Coroutines)

深入学习 C++20 协程(Coroutines) 头文件:<coroutine> 命名空间:std 编译器要求:GCC 11+ / Clang 14+ / MSVC 19.28+(均需 -std=c++20 或以上) 注意:GCC 10 / Clang 8~13 可通过 -fcoroutines 和 <experimental/coroutine> 使用实验性支持 一、为什么需要协程? 1.1 异步编程的传统痛点 游戏服务器中充斥着异步操作——数据库查询、网络 I/O、定时器回调。传统方案各有各的痛: 方案 A:回调地狱(Callback Hell) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void HandleLogin(Connection* conn, const LoginPacket& pkt) { // 第1步:查询数据库验证账号 dbManager->QueryAsync("SELECT * FROM accounts WHERE name=?", pkt.name, [conn, pkt](const DBResult& result) { if (!result.ok) { conn->SendError("DB错误"); return; } // 第2步:查询角色列表 dbManager->QueryAsync("SELECT * FROM characters WHERE account_id=?", result.accountId, [conn](const DBResult& charResult) { if (!charResult.ok) { conn->SendError("DB错误"); return; } // 第3步:加载角色数据 dbManager->QueryAsync("SELECT * FROM inventory WHERE char_id=?", charResult.charId, [conn, charResult](const DBResult& invResult) { // 第4步:终于可以发送登录成功了... conn->SendLoginSuccess(charResult, invResult); }); }); }); } 方案 B:状态机(State Machine) ...

April 8, 2026 · 22 min · 4611 words

深入学习 C++17 PMR(Polymorphic Memory Resource)

深入学习 C++17 PMR(Polymorphic Memory Resource) 头文件:<memory_resource> 命名空间:std::pmr 编译器要求:GCC 9+ / Clang 9+ / MSVC 19.13+(均需 -std=c++17 或以上) 一、为什么需要 PMR? 1.1 传统 Allocator 模型的痛点 C++98 引入的 Allocator 是模板参数,这意味着: 1 2 3 4 std::vector<int, MyAlloc<int>> vec1; std::vector<int, std::allocator<int>> vec2; // vec1 和 vec2 是不同类型!无法互相赋值、放进同一个容器 核心问题: 痛点 说明 类型传染 Allocator 是模板参数,换一个 Allocator 就变了类型,所有接口签名都要跟着改 无法运行时切换 编译期绑定,测试时想换成 debug allocator?重新编译 难以组合 想让 vector 内部的 string 也用同一个 arena?极其繁琐 状态传播困难 有状态 allocator(如持有内存池指针)在容器拷贝/移动时语义复杂 1.2 PMR 的解法:运行时多态 PMR 用一个虚基类 std::pmr::memory_resource 取代模板参数,容器统一使用 std::pmr::polymorphic_allocator<T>: ...

April 6, 2026 · 12 min · 2482 words

Docker 新手入门:从零开始容器化你的应用

Docker 新手入门:从零开始容器化你的应用 如果你的程序在你电脑上能跑,那就把你的电脑也一起发给客户吧。——Docker 之前的世界 写在前面 这篇文章适合谁? 听说过 Docker 但从未用过的开发者 被「在我电脑上明明能跑」折磨过的人 想了解容器化部署但不知道从哪开始的人 读完你将获得什么? 理解 Docker 核心概念(镜像、容器、仓库) 能独立编写 Dockerfile 并构建镜像 能用 Docker Compose 编排多容器应用 能将一个 Web 应用容器化部署 一、Docker 是什么? 1.1 一句话解释 Docker 是一个应用打包、分发、运行的平台。它把你的应用和所有依赖(库、配置、系统工具)打包成一个镜像,然后在任何安装了 Docker 的机器上以容器的形式运行。 1.2 虚拟机 vs 容器 对比项 虚拟机 (VM) Docker 容器 隔离级别 硬件级(Hypervisor) 进程级(内核共享) 启动速度 分钟级 秒级 体积 GB 级 MB 级 性能损耗 10-20% 接近原生 资源占用 高(每个 VM 一个完整 OS) 低(共享宿主内核) 1 2 3 4 5 6 7 8 9 10 11 12 13 ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ 虚拟机架构 │ │ Docker 架构 │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ App A │ App B │ App C │ │ App A │ App B │ App C │ │ Libs │ Libs │ Libs │ │ Libs │ Libs │ Libs │ │ OS │ OS │ OS │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Docker Engine │ │ Hypervisor │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Host OS │ │ Host OS │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Hardware │ │ Hardware │ └─────────────────────────────────┘ └─────────────────────────────────┘ 1.3 核心三概念 镜像(Image):只读模板,包含运行应用所需的一切。类比:安装光盘 容器(Container):镜像的运行实例。类比:用光盘装好的一台电脑 仓库(Registry):存放镜像的地方。类比:应用商店(Docker Hub) 三者关系: ...

October 1, 2025 · 10 min · 2113 words

现代 CMake 课程学习:从「面向目录」到「面向目标」

现代 CMake 课程学习:从「面向目录」到「面向目标」 现代 CMake 不是新语法,是新思维。 写在前面 这篇文章适合谁? 用过 CMake 但只会 add_executable + target_link_libraries 的人 从 Makefile / Visual Studio 工程迁移过来,想系统学 CMake 的人 看别人 CMakeLists.txt 里一堆 PUBLIC、$<BUILD_INTERFACE:...> 一头雾水的人 什么是 CMake?(30 秒版本) CMake 不是编译器,它是一个构建系统生成器。你写一份 CMakeLists.txt,CMake 帮你生成对应平台的构建文件: Linux → Makefile 或 Ninja Windows → Visual Studio .sln 或 Ninja macOS → Xcode 或 Ninja 类比:CMake 就像一个"翻译官",你用一种语言描述"我要编译什么",它翻译成各平台编译器能理解的指令。 为什么要学"现代" CMake? CMake 从 2000 年诞生至今,经历了巨大变化。2014 年的 CMake 3.0 是分水岭——引入了 target-based(面向目标)设计。此后的版本持续完善这套体系。 如果你还在用 include_directories()、link_libraries() 这套"传统写法",那你用的是 2014 年之前的思路——就像 2025 年还在写 C++98 一样。 ...

June 1, 2025 · 15 min · 3155 words

trantor 网络库学习总结

trantor 网络库学习总结 学习周期:一个多月 覆盖范围:trantor 全部核心模块,共 18 课 一、整体架构鸟瞰 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ┌─────────────────────────────────────────────────────────┐ │ 用户代码 / Drogon 框架 │ ├─────────────────────────────────────────────────────────┤ │ TcpServer / TcpClient │ │ • 连接管理(connSet_) • Round-Robin 分配 │ │ • TimingWheel 超时 • promise/future 优雅停止 │ ├────────────────┬────────────────────────────────────────┤ │ TcpConnection │ TaskQueue(Serial / Concurrent) │ │ • 状态机 │ • 卸载阻塞操作 │ │ • 发送队列 │ • SerialTaskQueue = EventLoopThread │ │ • TLS透明层 │ • ConcurrentTaskQueue = 线程池 │ ├────────────────┴────────────────────────────────────────┤ │ EventLoopThread / EventLoopThreadPool │ │ • 3阶段 promise/future 启动协议 │ │ • atomic round-robin 无锁分配 │ ├──────────┬──────────────┬──────────────────────────────┤ │ Acceptor │ Connector │ Resolver(DNS 异步解析) │ │ idleFd_ │ EINPROGRESS │ • NormalResolver(线程池) │ │ EMFILE │ 指数退避 │ • AresResolver(c-ares) │ ├──────────┴──────────────┴──────────────────────────────┤ │ EventLoop(Reactor 核心) │ │ loop() ← Channel ← Poller(epoll/kqueue/IOCP) │ │ runInLoop / queueInLoop / runAfter / runEvery │ │ MpscQueue<Func>:无锁任务投递 │ ├──────────────────────────┬──────────────────────────────┤ │ 定时器系统 │ 工具层 │ │ TimerQueue(最小堆) │ MsgBuffer / Logger │ │ TimingWheel(O(1) 超时) │ ObjectPool / MpscQueue │ │ timerfd / wakeupFd 驱动 │ Hash / secureRandomBytes │ └──────────────────────────┴──────────────────────────────┘ ↓ OS:epoll / kqueue / IOCP / wepoll 二、18 课核心知识点速查 阶段一:基础工具层(第 1-4 课) 第 1 课 — 日志系统 LOG_INFO << "msg" 展开为 Logger(__FILE__, __LINE__).stream(),析构时刷出 FixedBuffer<N>:栈上固定缓冲,避免日志路径的堆分配 AsyncFileLogger:前台线程写入内存队列,后台线程批量刷盘(异步、不阻塞 I/O) 自定义输出:Logger::setOutputFunction(),可对接 ELK、syslog 等 第 2 课 — 消息缓冲区 MsgBuffer 双指针设计:_readIndex / _writeIndex,中间是可读数据,右侧是可写空间 prepend 区域(8字节):预留报头空间,避免插入时移动数据 readFd():readv + 栈上 65536 字节备用缓冲,单次 syscall 读取大量数据 BufferNode 4种子类:MemBufferNode、FileBufferNodeUnix、FileBufferNodeWin、AsyncStreamBufferNode 第 3 课 — 日期时间与工具函数 Date:微秒精度时间点(int64_t microSecondsSinceEpoch_),可作定时器 key Date::now() → gettimeofday / GetSystemTimeAsFileTime NonCopyable:= delete 拷贝构造和赋值,所有核心类的基类 第 4 课 — 回调类型定义 ConnectionCallback:连接建立/断开 RecvMessageCallback:收到数据(TcpConnectionPtr + MsgBuffer*) WriteCompleteCallback:发送缓冲区清空 TimerCallback:定时器触发 阶段二:Reactor 核心(第 5-8 课) 第 5 课 — EventLoop 核心循环:epoll_wait → 分发 Channel 事件 → 执行 pendingFunctors_ wakeupFd_(eventfd/pipe):跨线程唤醒阻塞的 epoll_wait runInLoop(f):当前线程直接执行;其他线程 → queueInLoop → 唤醒 → 下轮执行 MpscQueue<Func> funcs_:任务队列用无锁 MPSC 队列,多线程投递无锁 关键不变量:EventLoop 是单线程的,所有网络操作必须在其线程执行。 ...

April 15, 2025 · 6 min · 1152 words

密码学工具 — 哈希函数 & 安全随机数

第 18 课:密码学工具 — 哈希函数 & 安全随机数 对应源文件: trantor/utils/Utilities.h — 公开 API(Hash128/160/256、所有哈希函数、secureRandomBytes) trantor/utils/Utilities.cc — 无 TLS 后端时的纯 C 实现 trantor/utils/crypto/openssl.cc — OpenSSL 后端实现 trantor/utils/crypto/botan.cc — Botan 后端实现 trantor/utils/crypto/md5.h/cc — 内置 MD5(纯 C) trantor/utils/crypto/sha1.h/cc — 内置 SHA1(纯 C,公有域) trantor/utils/crypto/sha256.h/cc — 内置 SHA256(纯 C) trantor/utils/crypto/sha3.h/cc — 内置 SHA3-256(Keccak,纯 C) trantor/utils/crypto/blake2.h/cc — 内置 BLAKE2b-256(纯 C) 一、整体架构:三层后端选择 trantor 的密码学工具采用编译期后端切换设计,同一套 API 在三种环境下对应不同实现: 1 2 3 4 5 6 7 8 9 10 11 12 13 用户代码 │ ▼ trantor::utils::md5(data, len) ← 统一 API(Utilities.h) │ ├─ USE_OPENSSL 定义时 ──→ crypto/openssl.cc (OpenSSL EVP API) ├─ USE_BOTAN 定义时 ──→ crypto/botan.cc (Botan HashFunction) └─ 两者均无时 ──────────→ Utilities.cc (内置纯 C 实现) ├─ crypto/md5.cc ├─ crypto/sha1.cc ├─ crypto/sha256.cc ├─ crypto/sha3.cc └─ crypto/blake2.cc 设计原理: ...

April 12, 2025 · 11 min · 2189 words