C++中std::variant的使用详解和实战代码示例

 更新时间:2026年05月19日 09:00:37   作者:点云SLAM  
本文主要介绍了C++中std::variant的使用详解和实战代码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

std::variant 是 C++17 引入的一个类型安全的联合体(type-safe union),它可以在多个类型之间存储一个值,并在编译时进行类型检查。它是现代 C++ 类型擦除与泛型编程的核心工具之一,适用于构建可变类型结构、消息传递系统、状态机等。

一、基本概念

#include <variant>

std::variant<T1, T2, ..., Tn> v;
  • 类似于联合体 union,但类型安全。
  • std::variant 只能存储其中一个类型的值
  • 默认构造时,会初始化为第一个类型的默认值

二、核心操作

1. 构造与赋值

#include <iostream>
#include <variant>

int main() {
    std::variant<int, std::string> v;

    v = 42;                 // 存储 int
    std::cout << std::get<int>(v) << std::endl;

    v = "hello";            // 存储 string
    std::cout << std::get<std::string>(v) << std::endl;

    return 0;
}

2. 访问值:std::get<T>()或std::get<index>()

std::get<int>(v);              // 根据类型访问
std::get<0>(v);                // 根据索引访问(int 是第 0 个类型)

如果类型不匹配,将抛出 std::bad_variant_access 异常。

3. 判断当前存储的类型

if (std::holds_alternative<std::string>(v)) {
    std::cout << "v contains a string\n";
}

4. 获取当前类型索引

std::cout << "index = " << v.index() << std::endl;

三、使用std::visit实现模式匹配

#include <iostream>
#include <variant>

void printVariant(const std::variant<int, std::string>& v) {
    std::visit([](auto&& arg) {
        std::cout << "Value: " << arg << std::endl;
    }, v);
}

四、完整示例:表达式计算器

#include <iostream>
#include <variant>
#include <vector>

using Expr = std::variant<int, float, std::string>;

void processExpr(const Expr& e) {
    std::visit([](auto&& val) {
        using T = std::decay_t<decltype(val)>;
        if constexpr (std::is_same_v<T, int>)
            std::cout << "int: " << val << "\n";
        else if constexpr (std::is_same_v<T, float>)
            std::cout << "float: " << val << "\n";
        else if constexpr (std::is_same_v<T, std::string>)
            std::cout << "string: " << val << "\n";
    }, e);
}

int main() {
    std::vector<Expr> exprs = { 1, 3.14f, "hello" };

    for (const auto& e : exprs)
        processExpr(e);

    return 0;
}

五、进阶用法:递归类型(std::monostate+std::unique_ptr)

std::variant 不能直接定义递归类型(自身包含自身)。需要使用指针 + std::monostate

struct Node;

using Tree = std::variant<
    std::monostate,        // 空节点
    int,                   // 叶子
    std::unique_ptr<Node>  // 子树
>;

struct Node {
    Tree left;
    Tree right;
};

六、注意事项

项目说明
异常std::get<T>(v) 如果类型不对,会抛出 std::bad_variant_access
默认值默认初始化为第一个类型的默认构造值
索引v.index() 返回当前类型在 variant 中的索引
编译期匹配使用 if constexpr + std::visit 可实现类型安全分派

七、综合实战示例

下面是一个用 std::variant 实现的 有限状态机(FSM)+ 消息分发系统 的综合示例,适用于状态驱动的系统,如游戏角色状态、UI 状态、网络连接状态等。

示例目的结构

我们实现一个简单的 FSM:角色可以在 Idle, Moving, Attacking 三个状态间切换,接收 Message 控制状态转换。

1. 基础定义

#include <iostream>
#include <variant>
#include <string>

// 状态定义
struct Idle {};
struct Moving { std::string destination; };
struct Attacking { std::string target; };

// 消息定义
struct StartMoving { std::string destination; };
struct StopMoving {};
struct StartAttack { std::string target; };
struct StopAttack {};

// 状态类型和消息类型
using State = std::variant<Idle, Moving, Attacking>;
using Message = std::variant<StartMoving, StopMoving, StartAttack, StopAttack>;

2. 消息处理器(核心)

void handle_message(State& state, const Message& msg) {
    std::visit([&](auto&& current_state) {
        using Current = std::decay_t<decltype(current_state)>;

        std::visit([&](auto&& m) {
            using Msg = std::decay_t<decltype(m)>;

            if constexpr (std::is_same_v<Current, Idle>) {
                if constexpr (std::is_same_v<Msg, StartMoving>) {
                    std::cout << "Idle → Moving to " << m.destination << "\n";
                    state = Moving{m.destination};
                } else if constexpr (std::is_same_v<Msg, StartAttack>) {
                    std::cout << "Idle → Attacking " << m.target << "\n";
                    state = Attacking{m.target};
                }
            } else if constexpr (std::is_same_v<Current, Moving>) {
                if constexpr (std::is_same_v<Msg, StopMoving>) {
                    std::cout << "Moving → Idle\n";
                    state = Idle{};
                } else if constexpr (std::is_same_v<Msg, StartAttack>) {
                    std::cout << "Moving → Attacking " << m.target << "\n";
                    state = Attacking{m.target};
                }
            } else if constexpr (std::is_same_v<Current, Attacking>) {
                if constexpr (std::is_same_v<Msg, StopAttack>) {
                    std::cout << "Attacking → Idle\n";
                    state = Idle{};
                } else if constexpr (std::is_same_v<Msg, StartMoving>) {
                    std::cout << "Attacking → Moving to " << m.destination << "\n";
                    state = Moving{m.destination};
                }
            }
        }, msg);
    }, state);
}

3. 主程序(状态切换测试)

int main() {
    State state = Idle{};

    handle_message(state, StartMoving{"North"});
    handle_message(state, StartAttack{"Dragon"});
    handle_message(state, StopAttack{});
    handle_message(state, StartAttack{"Goblin"});
    handle_message(state, StartMoving{"East"});
    handle_message(state, StopMoving{});

    return 0;
}

输出示例

Idle → Moving to North
Moving → Attacking Dragon
Attacking → Idle
Idle → Attacking Goblin
Attacking → Moving to East
Moving → Idle

小结:此结构的优势

特性说明
类型安全std::variant 和 std::visit 编译期确定类型,无需 RTTI 或类型转换
易扩展增加新状态或消息只需添加结构体 + 分支处理
面向对象可替代可替代经典面向对象状态机中的虚函数分派
适合嵌入式/游戏无动态分配,运行时高效

总结

用法说明
std::variant<A, B>表示可以存储 A 或 B 类型的变量
std::get<T>(v)获取当前存储的值(类型匹配)
std::visit()模式匹配:对当前值执行对应操作
holds_alternative<T>()判断当前是否存储的是 T 类型

到此这篇关于C++中std::variant的使用详解和实战代码示例的文章就介绍到这了,更多相关C++ std::variant使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言示例讲解switch分支语句的用法

    C语言示例讲解switch分支语句的用法

    这篇文章主要为大家介绍了switch语句,switch语句是我们常见会用到的结构,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Qt6.0开发环境搭建步骤(图文)

    Qt6.0开发环境搭建步骤(图文)

    这篇文章主要介绍了Qt6.0开发环境搭建步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • c++中#include &lt;&gt;与#include""的区别详细解析

    c++中#include &lt;&gt;与#include""的区别详细解析

    <>先去系统目录中找头文件,如果没有在到当前目录下找。所以像标准的头文件 stdio.h、stdlib.h等用这个方法
    2013-10-10
  • OpenCV实现图像背景虚化效果原理详解

    OpenCV实现图像背景虚化效果原理详解

    相信用过相机的同学都知道虚化特效,这是一种使焦点聚集在拍摄主题上,让背景变得朦胧的效果。本文将详细介绍一些这一效果的实现原理以及代码,需要的可以参考一下
    2022-03-03
  • 详解C++中mutable的用法

    详解C++中mutable的用法

    这篇文章主要介绍了详解C++中mutable的用法,帮助大家更好的理解和学习C++,感兴趣的朋友可以了解下
    2020-08-08
  • C++数据结构分析多态的实现与原理及抽象类

    C++数据结构分析多态的实现与原理及抽象类

    继承就是可以直接使用前辈的属性和方法。自然界如果没有继承,那一切都是处于混沌状态。多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作
    2022-02-02
  • C语言中的强符号和弱符号介绍

    C语言中的强符号和弱符号介绍

    这篇文章主要介绍了C语言中的强符号和弱符号介绍,本文用多个实例来讲解强符号和弱符号,需要的朋友可以参考下
    2015-03-03
  • C++日志记录类实例解析

    C++日志记录类实例解析

    这篇文章主要介绍了C++日志记录类实例,代码功能非常实用,需要的朋友可以参考下
    2014-07-07
  • C++隐式转换问题分析及解决办法

    C++隐式转换问题分析及解决办法

    在本篇文章里小编给大家整理了关于C++隐式转换问题分析及解决办法,有需要的朋友们可以学习下。
    2020-02-02
  • C++精要分析decltype的作用及用法

    C++精要分析decltype的作用及用法

    decltype是C++11新增的一个关键字,和auto的功能一样,用来在编译时期进行自动类型推导。引入decltype是因为auto并不适用于所有的自动类型推导场景,在某些特殊情况下auto用起来很不方便,甚至压根无法使用
    2022-05-05

最新评论