标准库标头 <variant> (C++17)学习之variant类

此头文件是通用工具库的一部分。类模板 std::variant 表示一个类型安全的联合体(以下称“变体”)。一个 std::variant 的实例在任意时刻要么保有它的可选类型之一的值,要么在错误情况下无值(此状态难以达成,见 valueless_by_exception)。

与联合体类似,如果变体保有某个对象类型 T 的值,那么 T 的对象表示会在变体自身的对象表示中直接分配。不允许变体分配额外的(动态)内存。

变体不能保有引用、数组,或类型 void。

变体可以保有同一类型多于一次,而且可保有同一类型的不同 cv 限定版本。

与联合体在聚合初始化中的行为一致,默认构造的变体保有它的首个选项的值,除非该选项不可默认构造(此时该变体也不可默认构造)。可以用辅助类 std::monostate 使这种变化体可默认构造。

如果程序在没有提供任何模板实参的情况下实例化了 std::variant 的定义,那么程序非良构。此时可以使用 std::variant<std::monostate> 代替。

如果程序声明了 std::variant显式部分特化,那么程序非良构,不要求诊断。

成员函数

(构造函数)

构造 variant 对象
(公开成员函数)

(析构函数)

析构 variant 和它包含的值
(公开成员函数)

operator=

赋值 variant
(公开成员函数)
观察器

index

返回 variant 所保有可选项的零基索引
(公开成员函数)

valueless_by_exception

检查 variant 是否在非法状态
(公开成员函数)
修改器

emplace

原位构造 variant 中的值
(公开成员函数)

swap

与另一 variant 交换
(公开成员函数)
观览

visit

(C++26)

variant 所保有的实参调用提供的函数对象
(公开成员函数)

非成员函数

visit

(C++17)

以一或多个 variant 所保有的各实参调用所提供的函数对象
(函数模板)

holds_alternative

(C++17)

检查某个 variant 是否当前持有某个给定类型
(函数模板)

std::get(std::variant)

(C++17)

以给定索引或类型(如果类型唯一)读取 variant 的值,错误时抛出异常
(函数模板)

get_if

(C++17)

以给定索引或类型(如果唯一),获得指向被指向的 variant 的值的指针,错误时返回空指针
(函数模板)

operator==operator!=operator<operator<=operator>operator>=operator<=>

(C++17)(C++17)(C++17)(C++17)(C++17)(C++17)(C++20)

以所含值比较 variant 对象
(函数模板)

std::swap(std::variant)

(C++17)

特化 std::swap 算法
(函数模板)

辅助类

monostate

(C++17)

用作非可默认构造类型的 variant 的首个可选项的占位符类型
(类)

bad_variant_access

(C++17)

非法地访问 variant 的值时抛出的异常
(类)

variant_sizevariant_size_v

(C++17)

在编译时获得 variant 可选项列表的大小
(类模板) (变量模板)

variant_alternativevariant_alternative_t

(C++17)

在编译时获得按索引指定的可选项的类型
(类模板) (别名模板)

std::hash<std::variant>

(C++17)

std::variant 的散列支持
(类模板特化)

辅助对象

variant_npos

(C++17)

非法状态的 variant 的下标
(常量)

示例代码:

#include <cassert>
#include <iostream>
#include <string>
#include <variant>
#include <type_traits>
#include <iomanip>

std::ostream& operator<<(std::ostream& os, std::variant<int, std::string> const& va)
{
    os << ": { ";

    std::visit([&](auto&& arg)
        {
            using T = std::decay_t<decltype(arg)>;
            if constexpr (std::is_same_v<T, int>)
                os << arg;
            else if constexpr (std::is_same_v<T, std::string>)
                os << std::quoted(arg);
        }, va);

    return os << " };\n";
}

struct Demo {
    Demo(int) {}
    Demo(const Demo&) { throw std::domain_error("复制构造函数"); }
    Demo& operator= (const Demo&) = default;
};

int main()
{
    std::variant<int, float> v, w;
    v = 42; // v 含 int
    int i = std::get<int>(v);
    assert(42 == i); // 成功
    w = std::get<int>(v);
    w = std::get<0>(v); // 与前一行效果相同
    w = v; // 与前一行效果相同
    std::cout << "i==========================================" << i << "\n";
    //  std::get<double>(v); // 错误:[int, float] 中无 double
    //  std::get<3>(v);      // 错误:有效索引值为 0 与 1
    try
    {
        std::get<float>(w); // w 含 int 而非 float:会抛出异常
    }
    catch (const std::bad_variant_access& ex)
    {
        std::cout << ex.what() << '\n';
    }

    using namespace std::literals;

    std::variant<std::string> x("abc");
    // 转换构造函数在无歧义时起作用
    x = "def"; // 转换赋值在无歧义时亦起作用

    std::variant<std::string, void const*> y("abc");
    // 传递 char const * 时转换成 void const *
    assert(std::holds_alternative<void const*>(y)); // 成功
    y = "xyz"s;
    assert(std::holds_alternative<std::string>(y)); // 成功

    
    //index / get example
    std::variant<int, std::string> v1 = "abc", v2;
    std::cout << "v1.index========" << v1.index() << "\n";
    std::cout << "v2.index========" << v2.index() << "\n";
    std::cout << "v1==============" << std::get<std::string>(v1) << "\n";
    v1 = {};
    v2 = "ssss";
    std::cout << "v1.index========" << v1.index() << "\n";
    std::cout << "v2.index========" << v2.index() << "\n";
    v1 = 20;
    std::cout << "v1==============" << std::get<int>(v1) << "\n";
    std::string strV2 = std::get<std::string>(v2);
    std::cout << "strV2===========" << strV2 << "\n";
    
    //operator=  example 
    std::variant<int, std::string> a{ 2017 }, b{ "CppCon" };
    std::cout << "a=" << a << "b=" << b << '\n';

    std::cout << "(1) operator=( const variant& rhs )\n";
    a = b;
    std::cout << "a=" << a << "b=" << b << '\n';

    std::cout << "(2) operator=( variant&& rhs )\n";
    a = std::move(b);
    std::cout << "a=" << a << "b=" << b << '\n';

    std::cout << "(3) operator=( T&& t ), 其中 T 为 int\n";
    a = 2019;
    std::cout << "a=" << a << '\n';

    std::cout << "(3) operator=( T&& t ), 其中 T 为 std::string\n";
    std::string s{ "CppNow" };
    std::cout << "s=: " << std::quoted(s) << '\n';
    a = std::move(s);
    std::cout << "a=" << a << "s=: " << std::quoted(s) << '\n';

    //emplace example
    std::variant<std::string> v3;
    v3.emplace<0>("abc"); // OK
    std::cout << "std::get<0>(v3)==============" << std::get<0>(v3) << '\n';
    v3.emplace<std::string>("def"); // OK
    std::cout << "std::get<0>(v3)==============" << std::get<0>(v3) << '\n';

    std::variant<std::string, std::string> v4;
    v4.emplace<1>("ghi"); // OK
    std::cout << "std::get<1>(v4)==============" << std::get<1>(v4) << '\n';
    // v4.emplace<std::string>("abc"); -> 错误
    std::variant<std::string> v5 = "sccot";
    std::cout << "std::get<0>(v5)==============" << std::get<0>(v5) << '\n';
    v5.swap(v3);
    std::cout << "std::get<0>(v5)==============" << std::get<0>(v5) << '\n';
    std::cout << "std::get<0>(v3)==============" << std::get<0>(v3) << '\n';

    //valueless_by_exception example 
    std::variant<std::string, Demo> var{ "str" };
    assert(var.index() == 0);
    assert(std::get<0>(var) == "str");
    assert(var.valueless_by_exception() == false);

    try {
        var = Demo{ 666 };
    }
    catch (const std::domain_error& ex) {
        std::cout << "1) 异常: " << ex.what() << '\n';
    }
    assert(var.index() == std::variant_npos);
    assert(var.valueless_by_exception() == true);

    // 现在 var “无值”,此为 var 的初始化过程中引发的异常导致的非法状态。

    try {
        std::get<1>(var);
    }
    catch (const std::bad_variant_access& ex) {
        std::cout << "2) 异常: " << ex.what() << '\n';
    }

    var = "str2";
    assert(var.index() == 0);
    assert(std::get<0>(var) == "str2");
    assert(var.valueless_by_exception() == false);

    //holds_alternative example
    std::variant<int, std::string> v6 = "abc";
    std::cout << std::boolalpha
        << "变体持有 int? "
        << std::holds_alternative<int>(v6) << '\n'
        << "变体持有 string? "
        << std::holds_alternative<std::string>(v6) << '\n';

    //get_if example
    auto check_value = [](const std::variant<int, float>& v)
    {
        if (const int* pval = std::get_if<int>(&v))
            std::cout << "变体值: " << *pval << '\n';
        else
            std::cout << "获取值失败!" << '\n';
    };

    std::variant<int, float> v7{ 12 }, w7{ 3.f };
    check_value(v7);
    check_value(w7);

    std::cout << "hello world\n";
}

运行结果:

参考:

标准库标头 <variant> (C++17) - cppreference.com

std::variant - cppreference.com

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值