c++中如何使用std::variant_c++17类型安全联合体用法
技术百科
冰火之心
发布时间:2026-01-01
浏览: 次 应使用 std::variant 替代裸 union,它类型安全、自动管理生命周期;声明需显式列出非重复的可选类型,初始化支持默认、直接和 in_place_type 三种方式;读取必须用 std::get(可能抛异常)或更推荐的 std::visit(编译期全覆盖、无异常)。
直接用 std::variant 替代裸 union,它能自动管理类型生命周期、禁止非法访问,并在编译期约束可选类型——这是 C++17 引入类型安全联合体的唯一正统方式。
如何声明和初始化 std::variant
std::variant 是一个模板类,必须显式列出所有允许的类型。它不接受重复类型,也不支持 void 或引用类型(但可存 int& 的包装如 std::reference_wrapper)。
常见错误:写成 std::variant(重复类型编译失败),或试图 std::variant(auto 不合法)。
初始化方式有三种:
- 默认构造:仅当首个类型有默认构造函数时才可行,例如
std::variant默认构造为int{} - 直接初始化:如
std::variant,编译器按参数类型推导并调用对应分支v{42}; - 使用
std::in_place_type_t显式指定:如std::variant<:string double> v{std::in_place_type<:string>, "hello"},适合需要带参构造的类型
如何安全读取 variant 中的值
不能像裸 union 那样直接 reinterpret_cast 或强制访问。必须通过 std::get 或 std::visit —— 否则触发未定义行为(UB)。
std::get 在运行时检查当前存储类型是否为 T,若不是则抛出 std::bad_variant_access。适用于已知类型且愿意处理异常的场景。
std::visit 是更推荐的方式,它把类型分发逻辑交给编译器,天然覆盖所有可能分支
,且无异常开销:
std::variantv = 3.14; std::visit([](const auto& x) { using T = std::decay_t ; if constexpr (std::is_same_v ) { std::cout << "int: " << x << "\n"; } else if constexpr (std::is_same_v ) { std::cout << "double: " << x << "\n"; } else if constexpr (std::is_same_v ) { std::cout << "string: " << x << "\n"; } }, v);
注意:lambda 必须是泛型(auto&),且内部要用 if constexpr 做编译期分支,否则会实例化所有分支导致编译失败(比如对 int 调用 .size())。
如何判断当前存储的是哪种类型
用 v.index() 获取当前类型的零基序号(从 0 开始),或用 v.valueless_by_exception() 判断是否因异常中途构造失败(极少见,通常发生在某类型移动构造抛异常时)。
更实用的是 std::holds_alternative,返回 bool 表示是否正存储 T:
- 比
v.index() == 1更可读、不易出错 - 可用于条件逻辑,例如:
if (std::holds_alternative<:string>(v)) { /* 安全 get */ } - 但它只是“快照”,无法防止其他线程并发修改
v;多线程下仍需额外同步
std::variant 和 union 的关键差异与陷阱
它不是语法糖,底层实现通常包含一个类型标签 + 内联缓冲区(类似 std::optional),所以大小是各类型大小的最大值加上一个字节(用于 tag)。这意味着:
- 大对象(如含 1KB 缓冲的结构体)会使整个
std::variant变得臃肿,慎用于高频小对象场景 - 移动语义被正确实现:移动后源
variant进入 valueless 状态(v.valueless_by_exception() == true),再次访问前必须重赋值 - 不支持
constexpr构造(C++20 起部分支持),若需编译期确定类型,请考虑std::variant配合if constexpr,而非运行时std::visit
真正难处理的点不在语法,而在于类型集合设计:一旦定义了 std::variant,后续新增类型就必须改所有 std::visit 分支和 std::holds_alternative 判断——这本质上是一种有限制的代数数据类型(ADT),扩展性弱于动态类型系统,但换来的是编译期安全和零运行时成本。
# 的是
# 是一种
# 是一个
# 可选
# 这是
# 也不
# 并在
# 适用于
# 三种
# 要用
# app
# auto
# 并发
# 对象
# c++
# if
# int
# void
# 泛型
# 构造函数
# access
# 线程
# 多线程
# 结构体
# 数据类型
# 引用类型
# Lambda
# bool
# union
# 存储类
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- C++ static_cast和dynamic_c
- Win11怎么更改计算机名_Windows11系统
- 如何使用正则表达式批量替换重复的星号-短横模式为固
- c++ unordered_map怎么用 c++哈
- Python随机数生成_random模块说明【指导
- Win10任务栏天气和资讯怎么关闭 Win10禁用
- 如何使用Golang模拟请求超时_Golang c
- 如何使用Golang实现聊天室消息存档_存储聊天记
- Linux怎么设置磁盘配额_Linux系统Quot
- MAC如何启用访达侧边栏显示_MAC Finder
- c++怎么实现高并发下的无锁队列_c++ std:
- 如何使用Golang安装依赖库_管理模块和第三方包
- windows如何修改文件默认打开方式_windo
- SAX解析器是什么,它与DOM在处理大型XML文件
- php打包exe后无法写入文件_权限问题解决方法【
- Win11怎么设置默认邮件应用_Windows11
- Go语言中slice追加操作的底层共享机制详解
- Python装饰器设计思路_功能增强机制说明【指导
- Linux如何挂载新硬盘_Linux磁盘分区格式化
- c++的STL算法库find怎么用 在容器中查找指
- Win11怎么设置按流量计费_Win11限制后台流
- Linux如何安装Golang环境_Linux下G
- Win10怎样卸载iTunes_Win10卸载iT
- Mac版Final Cut Pro入门_Mac视频
- Win10如何卸载预装Edge扩展_Win10卸载
- Win11此电脑不在桌面上_Windows 11桌
- XSLT怎么生成动态的HTML属性名和标签名
- c++ std::atomic如何保证原子性 c+
- MAC怎么使用表情符号面板_MAC Emoji快捷
- Win11怎么设置环境变量_Win11配置Path
- Win10如何更改网络连接_Windows10以太
- 短链接怎么用php还原_从基础原理到代码实现教学【
- php转mp4怎么设置帧率_调整php生成mp4视
- Windows10系统怎么查看运行时间_Win10
- Win11蓝牙开关不见了怎么办_Win11蓝牙驱动
- Win10怎样设置多显示器_Win10多显示器扩展
- LINUX如何查看文件类型_Linux中file命
- Windows蓝屏错误0x0000001E怎么修复
- 如何用正则表达式精确匹配最多含一个换行符的起止片段
- Win11用户账户控制怎么关_Win11关闭UAC
- 如何使用Golang实现容器健康检查_监控和自动重
- Win11怎么设置多显示器任务栏 Win11扩展任
- Windows10如何更改盘符名称_Win10重命
- Win10如何卸载WindowsDefender_
- Go 中 := 短变量声明的类型推导机制详解
- 如何使用Golang反射将map转换为struct
- Win11任务栏怎么固定应用 Win11将软件图标
- Windows10怎么查看系统激活状态_Windo
- mac怎么看硬盘大小_MAC查看磁盘存储空间与文件
- php内存溢出怎么排查_php内存限制调试与优化方

QQ客服