当前位置: 首页 > news >正文

现代嵌入式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
http://www.jsqmd.com/news/355293/

相关文章:

  • 我工具注册正确,模型也是官方推荐,为什么能执行blender工具,不能执行网页工具
  • 亲测好用! 一键生成论文工具 千笔·专业论文写作工具 VS 知文AI 专科生专属
  • FPGA 上用纯 Verilog 实现 H.264/AVC 视频解码的奇妙之旅
  • static关键字详解
  • (一)调包侠的思考与计划 - Ladisson
  • 北京上门收画|丰宝斋老字号护航,上门便捷化,交易透明化,藏家变现无忧 - 品牌排行榜单
  • 百年皇室安防,焕新海棠风采 集宝保险柜三亚海棠故事专柜盛大启幕 - 中媒介
  • 2025年12月 GESP CCF编程能力等级认证C++三级真题
  • 2.7假期记录
  • Zed IDE配置指南:打造高效的日常开发环境
  • 【你奶奶都能听懂的C语言】学习篇 第12期 字符处理函数+内存函数
  • 云服务合规:AWS/Azure测试数据驻留指南
  • 【C++】揭秘类与对象的内在机制
  • 现代python安装与管理方法——python-manager
  • 医疗设备测试:FDA法规与ISO 13485整合
  • Protobuf协议
  • 学生党平价首选!高性价比油皮洁面推荐,敏感肌也能放心用 - 资讯焦点
  • COPPA标准在APP测试中的技术实施框架
  • 内蒙古大学计算机研究生老导师详情
  • 网址链接
  • 开源许可证合规:测试工具选型的法律陷阱
  • 【嵌入式就业5】硬件体系与RTOS核心机制:从ARM架构到实时调度
  • GDPR下的测试日志管理:构建合规高效的自动化防护体系
  • 翠韵逐光,琼岛启新 佛山翠升生珠宝翡翠三亚海棠盛大启幕 - 中媒介
  • 2026年防腐蚀工业载冷剂厂家推荐指南 - 资讯焦点
  • 2026国内最新家电售后外包/家电售后安装维修服务商首选推荐神州联保:数字化服务引领者,神州联保值得信赖 - 品牌推荐2026
  • Kong + Consul 实现 网关服务和服务发现
  • 2026年软件测试公众号热度趋势与AI框架实战解析
  • 十四连冠的底蕴:解码格力中央空调全产业链掌控力 - 资讯焦点
  • C语言内存函数(二)