std::visit

来自cppreference.com
< cpp‎ | utility‎ | variant
 
 
工具库
通用工具
格式化库 (C++20)
(C++11)
关系运算符 (C++20 中弃用)
整数比较函数
(C++20)(C++20)(C++20)
(C++20)
swap 与类型运算
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
常用词汇类型
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)

初等字符串转换
(C++17)
(C++17)
栈踪
 
 
定义于头文件 <variant>
template <class Visitor, class... Variants>
constexpr /*see below*/ visit(Visitor&& vis, Variants&&... vars);
(1) (C++17 起)
template <class R, class Visitor, class... Variants>
constexpr R visit(Visitor&& vis, Variants&&... vars);
(2) (C++20 起)

应用观览器 visvariant 变量组 vars

这些重载仅若 std::remove_reference_t<Variants>... 中的每个类型均为(可为 const 限定的) std::variant 的特化,或(可为 const 限定)的类 C ,满足恰有一个 std::variant 特化是 C 的基类且它是公开且无歧义的基类才参与重载决议。

等效于返回

std::invoke(std::forward<Visitor>(vis),
            std::get<is>(std::forward<VariantBases>(vars))...)

,其中 VariantBases... 中的每个类型是确定如上的唯一 std::variant 特化,除了若对应的实参拥有 const 限定类型,为左值或右值,则分别添加 const&&& 到它,而 is...std::forward<VariantBases>(vars).index()...

1) 如同用 decltype 从返回的表达式推导返回类型。若上述调用不是对于所有 variant 的所有可选项类型均为同一类型和值类别的合法表达式,则调用为谬构。
2) 返回类型为 R 。若 R 是(可有 cv 限定的) void ,则舍弃 invoke 表达式的结果。

参数

vis - 接受每个 variant 的每个可能可选项的可调用 (Callable) 对象
vars - 传递给观览器的 variant 列表

返回值

1) 观览器的所选调用所返回的值,转换成所有可行的 std::invoke 表达式的共用类型。
2)R 为(可有 cv 限定的) void 则为无;否则为选择的观览器调用所返回的值,再隐式转换成 R

异常

若任何 vars 中的 variant 为因异常无值( valueless_by_exception ),则抛出 std::bad_variant_access 。如同通过 (std::forward<VariantBases>(vars).valueless_by_exception() || ...) 确定任何 variant 为因异常无值。

复杂度

variant 的数量为零或一时,可调用对象的调用在常数时间内实现,即它不取决于 sizeof...(Types)

variant 的数量大于 1 ,则可调用对象的调用没有复杂度要求。

注解

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

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

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

示例

#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
 
// 要观览的 variant
using var_t = std::variant<int, long, double, std::string>;
 
// 观览器 #3 的辅助常量
template<class> inline constexpr bool always_false_v = false;
 
// 观览器 #4 的辅助类型
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// 显式推导指引( C++20 起不需要)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
 
int main() {
    std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
    for(auto&& v: vec) {
        // 1. void 观览器,仅为其副效应调用
        std::visit([](auto&& arg){std::cout << arg;}, v);
 
        // 2. 返回值的观览器,返回另一 variant 的常见模式
        var_t w = std::visit([](auto&& arg) -> var_t {return arg + arg;}, v);
 
        std::cout << ". After doubling, variant holds ";
        // 3. 类型匹配观览器:亦能为带 4 个重载的 operator() 的类
        std::visit([](auto&& arg) {
            using T = std::decay_t<decltype(arg)>;
            if constexpr (std::is_same_v<T, int>)
                std::cout << "int with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, long>)
                std::cout << "long with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, double>)
                std::cout << "double with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, std::string>)
                std::cout << "std::string with value " << std::quoted(arg) << '\n';
            else 
                static_assert(always_false_v<T>, "non-exhaustive visitor!");
        }, w);
    }
 
    for (auto&& v: vec) {
        // 4. 另一种类型匹配观览器:有三个重载的 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. After doubling, variant holds int with value 20
15. After doubling, variant holds long with value 30
1.5. After doubling, variant holds double with value 3
hello. After doubling, variant holds std::string with value "hellohello"
10 15 1.500000 "hello"

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

DR 应用于 出版时的行为 正确行为
LWG 3052 C++17 Variants 中的任何类型不是 std::variant 则效果未指定 已指定

参阅

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