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++中的constexpr和const有什么区别
- Windows10如何更改鼠标图标_Win10鼠标
- 如何在 Go 后端安全获取并验证前端存储的 JWT
- c++的位运算怎么用 与、或、异或、移位操作详解【
- 如何使用Golang实现路由参数绑定_使用Mux和
- Go 中 defer 语句在 goroutine
- Drupal 中 HTML 链接被双重转义导致渲染
- c++ stringstream用法详解_c++字
- Win11怎么设置任务栏大小_Windows11注
- PHP主流架构怎么处理表单验证_规则与自定义【技巧
- Win10如何设置双wan路由器 Win10双wa
- C++中的Pimpl idiom是什么,有什么好处
- windows如何测试网速_windows系统网络
- Python网页解析流程_html结构说明【指导】
- Mac的“预览”如何合并多个PDF_Mac文件处理
- Win11怎么关闭防火墙通知_屏蔽Win11安全中
- Win11怎么更改输入法顺序_Win11调整语言首
- php485能和物联网模块通信吗_php485对接
- Windows Defender扫描失败怎么办_安
- Win11怎么更改默认打开方式_Win11关联文件
- Python路径拼接规范_跨平台处理说明【指导】
- Win11怎么关闭通知消息_屏蔽Windows 1
- Python类装饰器使用_元编程解析【教程】
- PythonPandas数据分析项目教程_时间序列
- Windows蓝屏BAD_POOL_HEADER故
- Win10怎么卸载金山毒霸_Win10彻底卸载金山
- Win11关机快捷键是什么_Win11快速关机方法
- 如何在 Django 中修改用户密码后保持会话不丢
- c# 如何用c#实现一个支持优先级的任务队列
- Win11怎么设置应用分屏_Windows11贴靠
- 如何使用Golang构建基础消息队列模拟_Gola
- Win11怎么设置任务栏透明_Windows11使
- c++20的std::format怎么用 比pri
- 如何使用Golang实现聊天室消息存档_存储聊天记
- 如何在 Django 中安全修改用户密码而不使会话
- 如何使用Golang理解结构体指针方法接收者_Go
- php订单日志权限怎么设_php订单日志文件权限设
- c++中explicit(bool)的用法 c++
- php会话怎么开启_session_start函数
- Win10怎么卸载剪映_Win10彻底卸载剪映方法
- Win11怎么开启游戏模式_Win11优化游戏帧数
- Win11无法安装软件怎么办_Win11解除应用安
- php在Linux怎么部署_LNMP环境搭建PHP
- Win11怎么关闭触控板_Win11笔记本禁用触摸
- c++输入输出流 c++ cin与cout格式化输
- Go语言中slice追加操作的底层共享机制解析
- PHP主流架构怎么监控运行状态_工具推荐【操作】
- Win11如何隐藏桌面图标 Win11一键隐藏/显
- Win10如何备份驱动程序_Win10驱动备份步骤
- Win11怎么关闭触摸键盘图标_Windows11

QQ客服