C++ std::variant 与 std::visit:安全保存多种类型,写清每个处理分支

C++ std::variant 与 std::visit:安全保存多种类型,写清每个处理分支

一个配置值可能是整数,也可能是字符串;一条消息可能是登录请求,也可能是退出通知。如果候选类型在编译期已知,C++17 的 std::variant 能用一个类型安全的对象表达"这些类型中的一个",不必自己维护 union 和类型标记。

1. 先判断,再按类型取值

cpp 复制代码
#include <iostream>
#include <string>
#include <variant>

int main() {
    std::variant<int, std::string> value = std::string("ready");
    if (auto text = std::get_if<std::string>(&value)) {
        std::cout << *text << std::endl;
    }
    value = 42; // 切换到整数分支
    if (std::holds_alternative<int>(value)) {
        std::cout << std::get<int>(value) << std::endl;
    }
}

get_if 接收 variant 指针,当前类型不匹配时返回空指针,适合条件分支。get 在类型不匹配时会抛出 std::bad_variant_access。按类型访问时,该类型必须在候选列表中恰好出现一次。

从 get_if 得到的指针是在借用内部对象。如果 variant 被销毁,或切换类型导致原对象销毁,就不能再继续使用旧指针。

2. std::visit:把每种类型的处理放在一起

下面是独立的 C++17 程序。Overloaded 把多个 lambda 的调用运算符合到一个访问器中,让不同类型走不同分支。

cpp 复制代码
#include <iostream>
#include <string>
#include <variant>

template<class... Ts>
struct Overloaded : Ts... {
    using Ts::operator()...;
};
template<class... Ts>
Overloaded(Ts...) -> Overloaded<Ts...>;

int main() {
    std::variant<int, std::string> value = std::string("ready");
    std::visit(Overloaded{
        [](int number) {
            std::cout << "number: " << number << std::endl;
        },
        [](const std::string& text) {
            std::cout << "text: " << text << std::endl;
        }
    }, value);
}

访问器必须能处理所有可能的分支。上面两个分支都返回 void。C++17 中常用的 std::visit 调用还要求各分支返回类型和值类别一致;一个返回 int、另一个返回 string,不能直接当成同一种返回结果。

3. 默认值不等于"没有值"

variant 默认构造时会默认构造第一个候选类型;例如以 int 为第一个类型时,通常得到整数零,而不是空状态。若需要显式表示"尚未设置",可以把 std::monostate 放在第一位:

cpp 复制代码
std::variant<std::monostate, int, std::string> config;
// 默认处于 monostate 分支;此处是示意片段,需要相应头文件

给它写 visit 时,也要处理 monostate。另一个需要知道的边界是:某些切换类型操作在构造抛异常时,variant 可能进入 valueless_by_exception 状态。它与主动设计的 monostate 不一样;对这种无值状态调用 visit 会抛出 bad_variant_access。

4. 速查

需求 推荐接口
判断当前类型 holds_alternative
类型匹配就取得指针 get_if
明确知道类型并接受异常检查 get
统一处理所有类型 visit
需要业务上的"未设置" 增加 monostate 分支

variant 适合有限、已知的候选类型集合。把每种状态的处理规则写完整,比散落的类型判断和强制转换更容易维护。

相关推荐
小羊没烦恼!1 小时前
在Scrum中实施敏捷建模
java·开发语言·windows·算法·c#
❀͜͡傀儡师1 小时前
20 年沉淀,CAS 8.0 重新定义企业级 SSO:适配 JDK 25 与 Spring Boot 4.1
java·开发语言·spring boot
程序员老陆1 小时前
WIN32_LEAN_AND_MEAN:Windows 头文件里的“精简编译开关”
c++·windows
weixin_307779131 小时前
基于睿擎工业开发平台的预训练视觉模型轻量化适配与低代码部署优化
开发语言·算法
实心儿儿1 小时前
Qt — Qt 多线程
开发语言·qt
小小龙学IT2 小时前
Go 泛型(Generics)深度解析:从类型参数到生产实践
开发语言·数据库·golang
hold?fish:palm2 小时前
45 二叉树的右视图
开发语言·c++·算法
MI_farmer2 小时前
Anaconda 安装在 D 盘但虚拟环境跑到 C 盘,以及 Conda PermissionError: WinError 5 的解决方法
c语言·开发语言·conda
“AI国潮设计-小江”2 小时前
[AIGC实战] 基于Stable Diffusion的潮汕非遗IP自动化生成工作流(附Python批量处理脚本)
开发语言·人工智能·python·prompt·aigc