Namespaces
Variants

std:: visit

From cppreference.net
Utilities library
定义于头文件 <variant>
template < class Visitor, class ... Variants >
constexpr /* 见下文 */ 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... > 的类型),并返回具有相同常量限定符和值类别的 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 交换内容
(公开成员函数)