news 2026/5/1 4:51:32

现代嵌入式C++教程:std::variant

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
现代嵌入式C++教程:std::variant

现代嵌入式C++教程:std::variant


写这篇文章前请想像一个场景:你有一个盒子,有时候装int,有时候装std::string,有时候装别的东西。传统union是那种老派盒子——节省空间但没有标签,容易把int当成std::string来读,后果就是 undefined behavior。std::variant就是现代的智能盒子:带标签、会照顾持有对象的构造/析构,并且在你试图错误读取时会大声给你抛出异常。


基本概念

std::variant<Ts...>表示“在一时刻仅持有Ts...中某一个类型”的类型安全联合体。它记录当前持有哪一个类型,构造/析构正确处理,并且提供访问、检查和访问者(visitor)机制。


最简单的例子

#include<variant>#include<string>#include<iostream>intmain(){std::variant<int,std::string>v;// 默认构造,持有 int(索引0)v=42;// 现在持有 intstd::cout<<std::get<int>(v)<<"\n";// 42v=std::string("hello variant");// 现在持有 std::string// 安全访问:if(std::holds_alternative<std::string>(v)){std::cout<<std::get<std::string>(v)<<"\n";}// 推荐方式:使用 std::visit(统一处理所有可能类型)std::visit([](auto&&x){std::cout<<x<<"\n";},v);}
  • 默认构造会构造第一个备选类型(上例中是int)。
  • std::get<T>(v):尝试以T访问,若v当前不是T,会抛std::bad_variant_access
  • std::holds_alternative<T>(v):检查当前类型是否为T
  • std::visit(visitor, v):由 visitor(可调用对象)处理当前持有的值,是最推荐也最安全的访问方式。

为什么优于裸union

std::variant自动管理对象生命周期(会调用析构函数),在访问时进行类型检查(抛异常而不是 UB),并与std::visit配合能写出清晰的模式匹配风格代码。它也比std::any更“精确”——std::any可以装任意类型,但类型信息查找不如variant明确,且没有visit的静态分支优势。


推荐访问方式:std::visit与重载集合

std::visitvariant的中心舞台。要同时处理多种类型,一个常见用法是利用“重载集”技巧来把多个 lambda 拼成一个可调用对象:

#include<variant>#include<iostream>#include<string>template<class...Ts>structoverloaded:Ts...{usingTs::operator()...;};template<class...Ts>overloaded(Ts...)->overloaded<Ts...>;// C++17 方便的模板推导intmain(){std::variant<int,double,std::string>v=3.14;std::visit(overloaded{[](inti){std::cout<<"int: "<<i<<"\n";},[](doubled){std::cout<<"double: "<<d<<"\n";},[](conststd::string&s){std::cout<<"string: "<<s<<"\n";}},v);}

好处是清晰、类型安全,编译器会在你漏写某个类型时给出提示(视情况而定),读代码的人也能一眼看出每个分支要干什么。


常见误区与坑(务必读)

  1. 不要用std::get<T>在不确定类型时访问—— 它会抛std::bad_variant_access。用std::get_if<T>std::visit更安全。

  2. 默认构造选第一个类型——std::variant<int, std::string> v;会构造int(值为 0),不是空的。需要“空值”语义可用std::monostate作为第一种类型。

  3. 不能直接做递归variant——std::variant<int, std::variant<...>>会引起不完整类型问题。用std::unique_ptrstd::shared_ptr包装递归类型:

    structNode{std::variant<int,std::unique_ptr<Node>>next;};
  4. 引用类型问题——std::variant<int&, double&>是可写的但容易错。通常用std::reference_wrapper<T>或保存指针更明确。

  5. 异常与析构—— 如果替换当前持有类型时新类型的构造抛异常,variant保证要么保持原值,要么变成valueless_by_exception(可以用v.valueless_by_exception()检查)。此状态下std::visit将抛出bad_variant_access

  6. 大小(内存)——variant的大小由最“宽”类型决定(加上一点元数据),有时比单个类型要大。权衡内存与便利性。


进阶技巧

  • 安全获取指针std::get_if<T>(&v)返回T*nullptr,避免异常。
  • 按索引访问std::get<0>(v)直接按索引(不推荐,容易错位,但在模板编程中有用)。
  • in-place 构造v.emplace<std::string>("hello")可以避免拷贝/临时并直接在variant内构造目标类型。
  • 元编程查看类型std::variant_size_v<decltype(v)>std::variant_alternative_t<I, decltype(v)>
  • 处理无值状态:调用v.valueless_by_exception()检查是否处于异常导致的无值状态(极少见,但处理代码中可以考虑)。
  • noexcept/移动语义variant的移动/拷贝/析构是否noexcept取决于备选类型的对应操作是否noexcept。注意移动/拷贝开销可能来自某个昂贵备选类型。

std::variant用到项目里去

假设你在处理一个消息队列,消息有三种:心跳(Heartbeat)、文本(Text),和二进制(Blob)。std::variant非常适合:

#include<variant>#include<string>#include<vector>#include<iostream>structHeartbeat{intid;};structText{std::string s;};structBlob{std::vector<uint8_t>data;};usingMessage=std::variant<Heartbeat,Text,Blob>;voidprocess(Messageconst&m){std::visit(overloaded{[](Heartbeatconst&h){std::cout<<"HB "<<h.id<<"\n";},[](Textconst&t){std::cout<<"Text: "<<t.s<<"\n";},[](Blobconst&b){std::cout<<"Blob size: "<<b.data.size()<<"\n";}},m);}

优势是清晰、类型安全;如果增加一种消息类型,你需要在visit处显式处理(这通常是好事)。


std::variantstd::anyboost::variant的比较

  • std::variant类型列表固定、编译时已知、支持visit,最适合有限且已枚举的类型集合。
  • std::any:可装任意类型,运行时类型检查繁琐,不适合做“有穷多种可能”的替代。
  • boost::variantstd::variant的前身,功能类似,历史项目可能还在用,但新代码优先选标准库的std::variant
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 3:46:31

CANN模型转换:从框架模型到昇腾高效模型的全链路智能转换实战

CANN组织链接&#xff1a;https://atomgit.com/cann ops-nn仓库链接&#xff1a;https://atomgit.com/cann/ops-nn 当PyTorch模型转换失败率高达35%&#xff0c;当量化后精度损失超5%导致医疗诊断误判&#xff0c;当转换后模型在昇腾设备性能不及GPU——模型转换已成为AI落地的…

作者头像 李华
网站建设 2026/5/1 3:49:22

CANN模型训练:从单机单卡到千卡集群的全链路高效训练实战

CANN组织链接&#xff1a;https://atomgit.com/cann ops-nn仓库链接&#xff1a;https://atomgit.com/cann/ops-nn 当千亿参数MoE模型训练周期长达42天&#xff0c;当千卡集群通信开销吞噬68%训练时间&#xff0c;当单节点故障导致72小时训练成果归零——模型训练已成为AI创新的…

作者头像 李华
网站建设 2026/5/1 3:51:53

2025年12月 GESP CCF编程能力等级认证C++三级真题

答案和更多内容请查看网站&#xff1a;【试卷中心 -----> CCF GESP ----> C/C ----> 三级】 网站链接 青少年软件编程历年真题模拟题实时更新 2025年12月 GESP CCF编程能力等级认证C三级真题 一、单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;…

作者头像 李华
网站建设 2026/5/1 4:44:36

StreamingLLM:无需训练即可支持无限上下文的推理技术

StreamingLLM&#xff1a;无需训练即可支持无限上下文的推理技术 cann组织链接&#xff1a;https://atomgit.com/cann ops-nn仓库链接&#xff1a;https://atomgit.com/cann/ops-nn 本文将展示&#xff1a; 如何基于 CANN 原生能力&#xff0c;实现 StreamingLLM 的核心机制 —…

作者头像 李华
网站建设 2026/4/23 20:25:23

编程小白也能玩转Python!零基础就能变办公“效率刺客”

前言 你是不是也经常看到大神用Python几行代码搞定你一下午的工作&#xff0c;却总觉得那是程序员、算法工程师的专属领域&#xff1f;自己连print(“Hello World”)都没打过&#xff0c;是不是就只能旁观&#xff1f;别急——2025年&#xff0c;Python早已不是高不可攀的黑科技…

作者头像 李华