Namespaces
Variants

std::visit

来自 zh.cppreference.net
 
 
 
 
定义于头文件 <variant>
template< class Visitor, class... Variants >
constexpr /* see below */ visit( Visitor&& v, Variants&&... values );
(1) (自 C++17 起)
template< class R, class Visitor, class... Variants >
constexpr R visit( Visitor&& v, Variants&&... values );
(2) (自 C++20 起)
辅助模板
template< class... Ts >
auto&& as-variant( std::variant<Ts...>& value );
(3) (仅用于阐述*)
template< class... Ts >
auto&& as-variant( const std::variant<Ts...>& value );
(4) (仅用于阐述*)
template< class... Ts >
auto&& as-variant( std::variant<Ts...>&& value );
(5) (仅用于阐述*)
template< class... Ts >
auto&& as-variant( const std::variant<Ts...>&& value );
(6) (仅用于阐述*)

将访问器 v(一个可调用对象,可用 Variants 的任何类型组合调用)应用于 Variants values。

给定 VariantBases 作为 decltype(as-variant(std::forward<Variants>(values))...(一组 sizeof...(Variants) 类型):

1) 犹如通过以下方式调用 v

INVOKE(std::forward<Visitor>(v),
std::get<indices>(std::forward<VariantBases>(values))...)
, 其中 indices 是 as-variant(values).index()...。

2) 犹如通过以下方式调用 v

INVOKE<R>(std::forward<Visitor>(v),
std::get<indices>(std::forward<VariantBases>(values))...)
, 其中 indices 是 as-variant(values).index()...。

仅当 VariantBases 中的每个类型都是合法类型时,这些重载才会参与重载决议。如果 INVOKE 或 INVOKE<R>(自 C++20 起) 所指示的表达式非法,或者对于不同的 ,INVOKE 或 <INVOKE>R(自 C++20 起)indices 的结果具有不同的类型或值类别,则程序非良构。

3-6) 仅用于阐述的 as-variant 函数模板接受一个值,其类型可推导为 std::variant<Ts...>(即可以是 std::variant<Ts...> 或从 std::variant<Ts...> 派生的类型),并返回具有相同 const 限定符和值类别的 std::variant 值。
3,4) 返回 value。
5,6) 返回 std::move(value)。

参数

v - 一个 Callable 对象,可接受 Variants 中所有变体的所有可能选项
values - 要传递给访问者的变体列表

返回值

1) INVOKE 操作的结果。返回类型是通过对结果应用 decltype 获得的类型。
2) 若 R 为(可能带有 cv 限定符的) void 则无返回值;否则返回 INVOKE<R> 操作的结果。
3-6) 从 value 转换而来的 std::variant 值。

异常

若 as-variant ( value_i ) . valueless_by_exception ( ) 对 values 中的任何变体 value_i 为 true ,则抛出 std::bad_variant_access 。

复杂度

当变体数量为零或一时,可调用对象的调用以常数时间实现;即其执行时间不取决于变体所能存储的类型数量。

如果变体数量大于一,可调用对象的调用没有复杂度要求。

注释

设 n 为 ( 1 * ... * std:: variant_size_v < std:: remove_reference_t < VariantBases >> ) ,实现通常为每个 std::visit 的特化生成一个等价于(可能是多维)包含 n 个函数指针的数组的表,这种实现方式与 虚函数 的实现类似。

实现也可以为 std::visit 生成具有 n 个分支的 switch 语句 (例如,当 n 不大于 256 时,MSVC STL 实现会使用 switch 语句)。

在典型实现中,调用 v 的时间复杂度可视为等同于访问(可能为多维)数组元素或执行 switch 语句的时间复杂度。

特性测试宏 值 标准 功能特性
__cpp_lib_variant 202102L (C++23)
(DR17)
std::visit 用于派生自 std::variant 的类

示例

#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
// 要访问的 variant
using value_t = std::variant<int, long, double, std::string>;
// 访问器 #4 的辅助类型
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
// 显式推导指南(C++20 起不再需要)
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
int main()
{
    std::vector<value_t> vec = {10, 15l, 1.5, "hello"};
    for (auto& v: vec)
    {
        // 1. void 访问器,仅用于副作用(此处用于 I/O)
        std::visit([](auto&& arg){ std::cout << arg; }, v);
        // 2. 返回值访问器,演示返回另一个 variant 的惯用法
        value_t w = std::visit([](auto&& arg) -> value_t { return arg + arg; }, v);
        // 3. 类型匹配访问器:为每种类型提供不同处理的 lambda
        std::cout << "。翻倍后,variant 持有 ";
        std::visit([](auto&& arg)
        {
            using T = std::decay_t<decltype(arg)>;
            if constexpr (std::is_same_v<T, int>)
                std::cout << "值为 " << arg << " 的 int\n";
            else if constexpr (std::is_same_v<T, long>)
                std::cout << "值为 " << arg << " 的 long\n";
            else if constexpr (std::is_same_v<T, double>)
                std::cout << "值为 " << arg << " 的 double\n";
            else if constexpr (std::is_same_v<T, std::string>)
                std::cout << "值为 " << std::quoted(arg) << " 的 std::string\n";
            else
                static_assert(false, "非穷举访问器!");
        }, w);
    }
    for (auto& v: vec)
    {
        // 4. 另一个类型匹配访问器:包含 3 个重载 operator() 的类
        // 注意:`(auto arg)` 模板 operator() 在此情况下将绑定到 `int` 和 `long`,
        //       但若缺少它,`(double arg)` operator() *也会* 绑定到 `int` 和 `long`,
        //       因为两者均可隐式转换为 double。使用此形式时,需注意隐式转换的正确处理。
        std::visit(overloaded{
            [](auto arg) { std::cout << arg << ' '; },
            [](double arg) { std::cout << std::fixed << arg << ' '; },
            [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
        }, v);
    }
}

输出:

10。翻倍后,variant 持有值为 20 的 int
15。翻倍后,variant 持有值为 30 的 long
1.5。翻倍后,variant 持有值为 3 的 double
hello。翻倍后,variant 持有值为 "hellohello" 的 std::string
10 15 1.500000 "hello"

缺陷报告

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

缺陷报告 适用范围 发布时行为 正确行为
LWG 2970 C++17 重载版本 (1) 的返回类型未保留
INVOKE 操作结果的值类别
保留值类别
LWG 3052
( P2162R2 )
C++17 当 Variants 中存在非 std::variant
类型时行为未作规定
已作明确规定

参见

(C++26)
以 variant 持有的参数调用提供的函数对象
(公开成员函数)
与另一个 variant 交换内容
(公开成员函数)