应仅在状态间结构差异大、共享字段少于50%时用std::variant实现状态机;否则徒增编译负担、分散逻辑且易致运行时崩溃。

直接用 std::variant 实现状态机,只在状态携带**结构差异大、共享字段少于 50%** 的数据时才真正值得;否则就是徒增编译负担、分散逻辑、还容易踩运行时崩溃的坑。
什么时候该用 std::variant 而不是 enum class
核心看字段是否“按状态分裂”:
- ConnectingState 需要
std::string address和int retry_count - ConnectedState 需要
int sockfd和std::chrono::seconds timeout - IdleState 真的什么也不需要 → 定义为
struct Idle {};,别塞std::monostate或空optional
这时硬套 enum class State { Idle, Connecting, Connected }; + 一个大 struct 塞满 std::optional<:string>、std::optional… 会导致:
- 每次访问前必须手动判
has_value(),漏一次就是未定义行为 - 内存浪费(所有 optional 都占空间,哪怕当前是
Idle) - 默认构造语义模糊(
State{}到底代表什么?)
std::visit 必须显式覆盖所有分支,且参数要用 const auto&
泛型 lambda 看似简洁,但写错就编译失败——这不是缺陷,是保护机制。常见错误是删分支、加 else 应付编译器,结果退化回运行时风险。
正确写法必须同时满足三点:
- lambda 参数写成
const auto& s,避免auto&&触发意外移动(尤其当状态含std::string或std::vector) - 用
std::decay_t提取真实类型,否则引用类型不匹配,std::is_same_v永远为 false - 每个
if constexpr分支对应一个确切状态类型,不许漏、不许合并、不许用else替代
示例:
std::visit([](const auto& s) {
using T = std::decay_t<decltype>;
if constexpr (std::is_same_v<t idle>) {
// do nothing
} else if constexpr (std::is_same_v<t connecting>) {
std::cout ) {
std::cout <h3>状态转换不能靠字段级修改,必须原子赋值新对象</h3>
<p>常见误操作:<code>std::get<connecting>(state).address = new_addr;</connecting></code> —— 这绕过了 <code>std::variant</code> 的类型安全机制,且无法保证旧状态析构时机。</p>
<p>正确做法是:状态变更即构造新对象,一次性替换整个 <code>std::variant</code>:</p>
<ul>
<li>用 <code>state = Connecting{"127.0.0.1", 3};</code>,而非修改已有字段</li>
<li>若需复用旧状态部分数据(比如从 <code>Connecting</code> 进入 <code>Connected</code> 时保留 socket),应在构造 <code>Connected</code> 时显式传入,而不是依赖“共享内存”</li>
<li>禁止在 <code>std::variant</code> 中放裸指针(如 <code>std::variant<int std::string></int></code>),否则资源管理责任被踢给上层,极易泄漏</li>
</ul>
<h3>
<code>std::get<t></t></code> 是危险操作,仅限断言后临时调试</h3>
<p><code>std::get<connecting>(state)</connecting></code> 一旦当前不是 <code>Connecting</code>,立刻抛 <code>std::bad_variant_access</code> —— 这是运行时错误,测试难覆盖,线上才暴露。</p>
<p>它绕过编译器对分支完整性的检查,退化回传统 <code>switch</code> 的漏处理风险。真需要轻量判断,改用:</p>
<ul>
<li>
<code>if (auto* p = std::get_if<connecting>(&state)) { /* use *p */ }</connecting></code>,返回 <code>nullptr</code> 表示不匹配,安全但需手动判空</li>
<li>
<code>std::holds_alternative<connecting>(state)</connecting></code> 仅作类型检查,不取值</li>
</ul>
<p>主逻辑里绝不要出现 <code>std::get</code>,这是最常被忽略、也最容易引发线上崩溃的点。</p></t></t></decltype>
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











