**如何在 C++ 中使用 std::variant 实现类型安全的多态?**

在 C++17 之后,std::variant 为我们提供了一种类型安全的方式来存储多种可能的类型,类似于 “和类型”(union)的安全版。相比传统的基类指针或 std::anystd::variant 具有更好的类型检查、无运行时开销以及更易读的语义。本文将从基本使用到高级技巧,系统阐述如何用 std::variant 在 C++ 中实现类型安全的多态。


1. 基础语法与声明

#include <variant>
#include <string>
#include <iostream>
#include <vector>

using ShapeVariant = std::variant<int, double, std::string>;

std::variant 的模板参数列表表示它可以容纳的所有类型。构造时传入对应类型的值即可:

ShapeVariant v1 = 42;          // int
ShapeVariant v2 = 3.14;        // double
ShapeVariant v3 = std::string("circle"); // std::string

2. 访问值

2.1 std::get

如果你确定当前持有的类型,可以使用 `std::get

(v)`: “`cpp int i = std::get (v1); // 成功 // double d = std::get (v1); // 运行时抛出 bad_variant_access “` #### 2.2 `std::get_if` 更安全的方式是使用指针返回值,若类型不匹配则返回 `nullptr`: “`cpp if (auto p = std::get_if (&v1)) { std::cout #include #include struct Circle { double radius; }; struct Rectangle { double w, h; }; struct Triangle { double a, b, c; }; using Shape = std::variant; double area(const Shape& s) { return std::visit([](const auto& shape) -> double { using T = std::decay_t; if constexpr (std::is_same_v) { return M_PI * shape.radius * shape.radius; } else if constexpr (std::is_same_v) { return shape.w * shape.h; } else if constexpr (std::is_same_v) { double s = (shape.a + shape.b + shape.c) / 2; return std::sqrt(s * (s – shape.a) * (s – shape.b) * (s – shape.c)); } else { static_assert(always_false ::value, “Non-exhaustive visitor!”); } }, s); } int main() { Shape shapes[] = { Circle{5.0}, Rectangle{4.0, 6.0}, Triangle{3.0, 4.0, 5.0} }; for (const auto& shape : shapes) { std::cout ` 让 `std::monostate` 代表空状态。 | | **拷贝与移动** | `variant` 满足 `CopyConstructible` 与 `MoveConstructible`,但若其中包含不可拷贝类型,需要自行限制。 | | **访问性能** | `std::visit` 的调用会根据内部类型在编译期确定,通常无额外运行时开销;但如果有大量 `visit`,可考虑使用 `std::variant` 的 `index()` 与 `get()` 进行手工调度。 | | **递归 variant** | 直接声明 `std::variant>` 会导致无限递归;可用 `std::variant` 或 `std::variant`。 | | **std::visit 与多重重载** | 当需要不同参数列表时,可用 `std::visit` 的多重重载或 `std::variant` 的 `apply_visitor` 模式。 | | **错误处理** | `std::visit` 必须对所有可能类型都有对应处理,若遗漏会导致编译错误(`static_assert` 或编译警告)。 | — ### 5. 与传统多态比较 | 特点 | `std::variant` | 虚函数多态 | |——|—————-|————| | 运行时开销 | 常数(无虚表) | 指针间接访问 | | 类型安全 | 编译期检查 | 运行时错误可能 | | 可扩展性 | 添加类型需改 variant 声明 | 继承体系可动态扩展 | | 可移植性 | 标准 C++17 | 依赖 OOP 设计 | 在多态对象数量固定、可预知且不需要继承层次的场景,`std::variant` 是更简洁、更高效的选择。 — ### 6. 进一步阅读 1. 《C++17 标准库设计》 – 章节 20 讨论 `variant` 与 `visit` 的实现细节。 2. 《Effective Modern C++》 – 第 10 章强调使用 `variant` 替代 `any`。 3. 《C++ 设计模式》 – 适用于 `variant` 的“访问者模式”实现。 — **结语** `std::variant` 为 C++ 开发者提供了一种类型安全、零开销的多态实现方案。只需掌握基本的构造、访问与 `visit` 技巧,即可在项目中优雅地替代传统的虚函数层次结构。希望本文能帮助你在实际编码中快速上手并发挥 `variant` 的全部优势。

发表评论