0

0

C++中std::visit与std::variant怎么配合使用_C++17多态替代方案【设计】

冰火之心

冰火之心

发布时间:2026-02-06 16:02:32

|

768人浏览过

|

来源于php中文网

原创

std::visit 是访问 std::variant 值的强制要求,因 std::get 需编译期确定类型,否则抛 std::bad_variant_access 导致崩溃;正确做法是用 std::visit 统一分发并由编译器生成各类型的处理分支。

c++中std::visit与std::variant怎么配合使用_c++17多态替代方案【设计】

std::visit 是访问 std::variant 中当前持有值的唯一标准方式,不是可选技巧,而是强制要求——不通过 std::visit(或其变体如 std::holds_alternative + std::get)就无法安全取出值。

为什么不能直接用 std::get 访问 variant?

因为 std::get(v) 要求编译期确定 v 当前确实持有类型 T,否则抛出 std::bad_variant_access。而运行时类型是动态的,硬写 std::get(v) 很容易崩溃。

常见错误现象:std::terminate 被调用,控制台只显示 “terminate called after throwing an instance of 'std::bad_variant_access'” —— 这说明你绕过了类型检查,直接强取了错误分支。

正确做法是:先用 std::visit 统一分发,让编译器为你生成所有可能类型的处理分支:

立即学习C++免费学习笔记(深入)”;

std::variant v = 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 << "string: " << x << "\n";
    } else if constexpr (std::is_same_v) {
        std::cout << "double: " << x << "\n";
    }
}, v);

如何写一个能复用的 visitor 类?

Lambda 写多了会重复,尤其当多个地方要对同一 std::variant 做类似 dispatch 时。这时应定义具名 visitor 结构体,显式继承 std::variant 所有可选项的重载 operator():

  • 必须为 variant 中每种类型都提供一个 operator() 重载,否则编译失败(这是安全性的体现)
  • 避免使用模板 operator() 单一入口,除非配合 if constexpr,否则可能漏处理某类型
  • 如果某些类型处理逻辑相同,可以共用一个重载,比如 void operator()(const std::string&) constvoid operator()(const std::vector&) const 都转成 size 输出

示例:

Scrumball
Scrumball

AI驱动的网红营销平台

下载
struct Printer {
    void operator()(int i) const { std::cout << "int=" << i; }
    void operator()(const std::string& s) const { std::cout << "str=" << s; }
    void operator()(double d) const { std::cout << "dbl=" << d; }
};
// 使用
std::variant v = "hello";
std::visit(Printer{}, v); // 输出 str=hello

std::visit 的异常安全性与性能影响

std::visit 本身不抛异常(除非你 visitor 里主动 throw),但它内部会做一次运行时分支判断(通常编译为查表或条件跳转),开销极小,远低于虚函数调用或 dynamic_cast —— 因为 variant 的 type index 是内建存储的,无需 RTTI 查找。

注意两个易忽略点:

  • 如果 variant 是空状态(std::monostate 或 move 后未赋值),std::visit 仍会尝试调用 visitor,但不会匹配任何重载,导致编译失败;所以建议 variant 至少包含 std::monostate 作为默认项,并为其提供 operator()
  • visitor 对象若含状态(比如捕获外部变量的 lambda),需确认其生命周期覆盖 visit 调用;临时 lambda 没问题,但局部 struct 引用外部栈变量就危险

替代虚函数多态时的关键设计权衡

std::variant + std::visit 替代基类指针/引用 + 虚函数,本质是从“运行时开放扩展”转向“编译期封闭枚举”。这意味着:

  • 新增类型需修改 variant 定义、所有 visitor 实现、所有 switch-like 分支 —— 不适合插件化或未知子类场景
  • 没有对象生命周期管理开销(不用 new/delete,无虚表指针,缓存友好)
  • 编译器更容易内联 visitor 中的每个分支,实际性能常优于虚调用
  • 无法实现“向上转型”或“动态添加行为”,比如不能把 std::variant 隐式转成 std::variant

所以它不是万能多态替代品,而是当你明确知道所有可能类型、且追求零成本抽象时的精准工具。

最常被忽略的是:variant 的“类型集合”一旦固定,就锁死了整个访问契约。改一个类型,所有 visitor 都得同步更新——这既是约束,也是文档。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

585

2023.08.02

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

795

2023.08.22

switch语句用法
switch语句用法

switch语句用法:1、Switch语句只能用于整数类型,枚举类型和String类型,不能用于浮点数类型和布尔类型;2、每个case语句后面必须跟着一个break语句,以防止执行其他case的代码块,没有break语句,将会继续执行下一个case的代码块;3、可以在一个case语句中匹配多个值,使用逗号分隔;4、Switch语句中的default代码块是可选的等等。

545

2023.09.21

Java switch的用法
Java switch的用法

Java中的switch语句用于根据不同的条件执行不同的代码块。想了解更多switch的相关内容,可以阅读本专题下面的文章。

427

2024.03.13

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

17

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

17

2025.11.27

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

537

2023.09.20

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

282

2025.06.09

1688阿里巴巴货源平台入口与批发采购指南
1688阿里巴巴货源平台入口与批发采购指南

本专题整理了1688阿里巴巴批发进货平台的最新入口地址与在线采购指南,帮助用户快速找到官方网站入口,了解如何进行批发采购、货源选择以及厂家直销等功能,提升采购效率与平台使用体验。

57

2026.02.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 3.7万人学习

C# 教程
C# 教程

共94课时 | 8.7万人学习

C 教程
C 教程

共75课时 | 4.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号