命名空间
变体 (Variants)
操作 (Actions)

std::variant

来自 cppreference.cn
< cpp‎ | utility
 
 
 
 
定义于头文件 <variant>
template< class... Types >
class variant;
(自 C++17 起)

类模板 std::variant 表示类型安全的联合体

variant 的实例在任何给定时间要么保持其备选类型之一的值,要么在错误的情况下 - 没有值(此状态很难实现,请参见 valueless_by_exception)。

与联合体一样,如果变体持有某种对象类型 T 的值,则 T 对象嵌套在 variant 对象内。

变体不允许持有引用、数组或 void 类型。

变体允许多次持有相同类型,以及持有相同类型的不同 cv 限定版本。

聚合初始化期间联合体的行为一致,默认构造的变体持有其第一个备选类型的值,除非该备选类型不可默认构造(在这种情况下,变体也不可默认构造)。辅助类 std::monostate 可用于使此类变体可默认构造。

使用没有模板参数的 std::variant 定义实例化的程序是非良构的。可以使用 std::variant<std::monostate> 来代替。

如果程序声明 std::variant显式部分特化,则程序是非良构的,无需诊断。

内容

[编辑] 模板参数

Types - 可能存储在此变体中的类型。所有类型都必须满足 Destructible 要求(特别是,不允许使用数组类型和非对象类型)。

[编辑] 成员函数

构造 variant 对象
(公开成员函数) [编辑]
销毁 variant,以及其包含的值
(公开成员函数) [编辑]
赋值 variant
(公开成员函数) [编辑]
观察器
返回 variant 持有的备选类型的从零开始的索引
(公开成员函数) [编辑]
检查 variant 是否处于无效状态
(公开成员函数) [编辑]
修改器
variant 中就地构造一个值
(公开成员函数) [编辑]
与另一个 variant 交换
(公开成员函数) [编辑]
访问
(C++26)
使用 variant 持有的参数调用提供的仿函数
(公开成员函数) [编辑]

[编辑] 非成员函数

(C++17)
使用一个或多个 variant 持有的参数调用提供的仿函数
(函数模板) [编辑]
检查 variant 当前是否持有给定类型
(函数模板) [编辑]
给定索引或类型(如果类型是唯一的)读取变体的值,错误时抛出异常
(函数模板) [编辑]
(C++17)
给定索引或类型(如果类型是唯一的),获取指向所指向的 variant 值的指针,错误时返回 null
(函数模板) [编辑]
(C++17)(C++17)(C++17)(C++17)(C++17)(C++17)(C++20)
比较 variant 对象,如同比较它们包含的值
(函数模板) [编辑]
特化 std::swap 算法
(函数模板) [编辑]

[编辑] 辅助类

(C++17)
占位符类型,用作非默认构造类型的 variant 中的第一个备选类型
(类) [编辑]
在无效访问 variant 的值时抛出的异常
(类) [编辑]
在编译时获取 variant 的备选类型列表的大小
(类模板) (变量模板)[编辑]
在编译时获取由索引指定的备选类型的类型
(类模板) (别名模板)[编辑]
std::variant 的哈希支持
(类模板特化) [编辑]

[编辑] 辅助对象

处于无效状态的 variant 的索引
(常量) [编辑]

[编辑] 注解

特性测试 Std 特性
__cpp_lib_variant 201606L (C++17) std::variant: 类型安全的联合体
202102L (C++23)
(DR17)
std::visit 用于从 std::variant 派生的类
202106L (C++23)
(DR20)
完全 constexpr std::variant
202306L (C++26) 成员 visit

[编辑] 示例

#include <cassert>
#include <iostream>
#include <string>
#include <variant>
 
int main()
{
    std::variant<int, float> v, w;
    v = 42; // v contains int
    int i = std::get<int>(v);
    assert(42 == i); // succeeds
    w = std::get<int>(v);
    w = std::get<0>(v); // same effect as the previous line
    w = v; // same effect as the previous line
 
//  std::get<double>(v); // error: no double in [int, float]
//  std::get<3>(v);      // error: valid index values are 0 and 1
 
    try
    {
        std::get<float>(w); // w contains int, not float: will throw
    }
    catch (const std::bad_variant_access& ex)
    {
        std::cout << ex.what() << '\n';
    }
 
    using namespace std::literals;
 
    std::variant<std::string> x("abc");
    // converting constructors work when unambiguous
    x = "def"; // converting assignment also works when unambiguous
 
    std::variant<std::string, void const*> y("abc");
    // casts to void const* when passed a char const*
    assert(std::holds_alternative<void const*>(y)); // succeeds
    y = "xyz"s;
    assert(std::holds_alternative<std::string>(y)); // succeeds
}

可能的输出

std::get: wrong index for variant

[编辑] 缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 应用于 已发布行为 正确行为
LWG 2901 C++17 提供了 std::uses_allocator 的特化,
variant 无法正确支持分配器
特化已移除
LWG 3990 C++17 程序可以声明显式或
std::variant 的部分特化
在这种情况下,程序是非良构的
情况(无需诊断)
LWG 4141 C++17 存储的要求
分配令人困惑
包含的对象必须
嵌套在 variant 对象内

[编辑] 参见

就地构造标签
(标签)[编辑]
(C++17)
一个可能持有也可能不持有对象的包装器
(类模板) [编辑]
(C++17)
持有任何CopyConstructible 类型的实例的对象
(类) [编辑]