C++如何使用std::visit遍历std::variant?(C++17)

std::visit 遍历 std::variant 的核心是传入能处理其所有可能类型的可调用对象,自动匹配当前持有类型;常用带初始化捕获的 lambda,结合 if constexpr 实现编译期类型分发。

std::visit 遍历 std::variant 的核心是:传入一个可调用对象(比如 lambda、函数对象或普通函数),它能处理 std::variant 所有可能的类型。访问时自动匹配当前持有的类型,无需手动判断。

基本用法:用 lambda 处理每种可能类型

最常见也最简洁的方式是用带初始化捕获的 lambda,利用 C++17 的折叠表达式或分别写分支:

std::variant v = 42;

std::visit([](const auto& value) {
    using T = std::decay_t;
    if constexpr (std::is_same_v) {
        std::cout << "int: " << value << '\n';
    } else if constexpr (std::is_same_v) {
        std::cout << "string: " << value << '\n';
    } else if constexpr (std::is_same_v) {
        std::cout << "double: " << value << '\n';
    }
}, v);

这里用了 if constexpr,编译期只保留匹配分支,安全高效。

更简洁写法:重载 lambda 或使用 std::overload

避免冗长的 if constexpr,可以手写重载结构,或用辅助工具(如 std::overload)组合多个 lambda:

template struct overloaded : Ts... { using Ts::operator()...; };
template overloaded(Ts...) -> overloaded;

std::visit(overloaded{
    [](int i) { std::cout << "int: " << i << '\n'; },
    [](const std::string& s) { std::cout << "string: " << s << '\n'; },
    [](double d) { std::cout << "double: " << d << '\n'; }
}, v);

每个 lambda 对应一种类型,std::visit 自动选中当前值的对应重载。

注意 const 和引用限定符

如果 std::variant 是 const 或右值,lambda 参数也要匹配:

  • 对 const variant,lambda 参数建议用 const auto& 或具体 const 引用(如 const int&
  • 想修改 variant 中的值?不行 —— std::visit 提供的是只读访问;如需就地修改,得用 std::get_if 或直接赋值
  • 若 variant 含非拷贝类型(如 std::unique_ptr),用 auto&& 完美转发更安全

处理多参数 variant(C++17 起支持)

std::visit 可同时访问多个 variant(要求它们类型列表兼容):

std::variant v1 = 100;
std::variant v2 = "hello";

std::visit([](const auto& a, const auto& b) {
    std::cout << "v1=" << a << ", v2=" << b << '\n';
}, v1, v2); // 编译器会找出所有合法的 (T1,T2) 组合并生成对应调用

注意:只有当两个 variant 存在至少一组公共可调用签名时,才不会编译失败。

基本上就这些。关键不是“遍历”而是“访问当前值”,std::visit 是类型安全的访问机制,不涉及循环或迭代器 —— 它一次只作用于 variant 当前持有的那个值。