std::variant不是继承多态的替代品,而是适用场景完全不同的工具;它适用于有限、已知、值语义明确的类型集合,如配置解析、状态机、消息载荷,不适用于需动态扩展类型或运行时多态的场景。

直接说结论:std::variant不是继承多态的替代品,而是**适用场景完全不同的工具**;选错会导致类型爆炸、访问逻辑冗余,甚至掩盖设计缺陷。
什么时候该用 std::variant 而不是虚函数继承?
核心判断依据是:你是否在处理「有限、已知、值语义明确」的类型集合。
- 适合
std::variant的典型场景:配置项解析(int/double/std::string)、状态机状态(Idle/Running/Error)、消息体载荷(Json/Protobuf/RawBytes) - 不适合的场景:需要扩展新类型(比如未来加个
Circle就得改所有std::variant<...></...>模板参数)、需要运行时动态注册子类、有深度继承层次或共享基类接口行为 - 关键区别在于内存模型:
std::variant是栈上紧凑布局,无 vptr 开销;而继承多态每个对象带指针,且必须通过指针/引用调用虚函数
std::get<t></t> 和 std::visit 的误用陷阱
常见错误是把 std::get<t></t> 当作“安全取值”用,其实它根本不管当前 std::variant 里存的是不是 T —— 类型不匹配就抛 std::bad_variant_access 异常。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 不要写
std::get<int>(v)</int>前不检查,尤其在循环或用户输入路径中 - 优先用
std::get_if<t>(&v)</t>:返回T*,空指针表示类型不匹配,可安全判空 -
std::visit的 lambda 必须能处理std::variant所有备选类型,漏一个编译不过;若某类型需特殊逻辑,用if constexpr (std::is_same_v<t yourtype>)</t>分支,别用普通if
继承多态无法绕开的隐含成本
很多人忽略的是:虚函数调用本身不是瓶颈,但它的内存布局和缓存行为在高频小对象场景下很致命。
- 每个派生对象头部多一个指针(通常是 8 字节),对
std::vector<shape></shape>这类容器,指针间接跳转破坏 CPU 预取,cache line 利用率骤降 - 虚函数表是全局共享的,但调用路径依赖运行时对象状态,编译器几乎无法内联,而
std::visit中的 lambda 在多数情况下可被完全内联 - 析构必须是虚的,否则
delete base_ptr会漏掉派生类成员析构 —— 这个规则容易被遗忘,尤其在 RAII 资源管理类中
真正难的不是语法选择,而是识别出「这个需求本质是不是类型组合问题」。一旦把业务上本该正交的类型强行塞进继承体系,或者反过来,用 std::variant 去模拟开放扩展的插件系统,后续维护代价远超初期编码时间。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










