跳到主要内容

std::visit

定义于头文件 <variant>

声明

// 1)
template <class Visitor, class... Variants>
constexpr /* see below */ visit( Visitor&& vis, Variants&&... vars );
// 2)
template <class R, class Visitor, class... Variants>
constexpr R visit( Visitor&& vis, Variants&&... vars );
// 3)
template<class... Ts>
auto&& as-variant( std::variant<Ts...>& var ); // exposition only
// 4)
template<class... Ts>
auto&& as-variant( const std::variant<Ts...>& var ); // exposition only
// 5)
template<class... Ts>
auto&& as-variant( std::variant<Ts...>&& var ); // exposition only
// 6)
template<class... Ts>
auto&& as-variant( const std::variant<Ts...>&& var ); // exposition only

将访问器 `vis` (一个可调用对象,可以与来自变体的任何类型组合调用) 应用于变体 `vars`。

给定 `VariantBases` 为 decltype(as-variant(`std::forward(vars))... (一个包含 sizeof...(Variants) 个类型的包)

    1. 调用 `vis`,如同
INVOKE(std::forward<Visitor>(vis),
std::get<indices>(std::forward<VariantBases>(vars))...)

,其中 `indices` 是 `as-variant(vars).index()....`

    1. 调用 `vis`,如同
INVOKE<R>(std::forward<Visitor>(vis),
std::get<indices>(std::forward<VariantBases>(vars))...)

,其中 `indices` 是 `as-variant(vars).index()....`

只有当 `VariantBases` 中的每个类型都是有效类型时,这些重载才参与重载解析。如果 `INVOKE` 表达式(或 `INVOKE` (自 C++20 起))无效,或者 `INVOKE`(或 `INVOKE` (自 C++20 起))对于不同的索引具有不同的类型或值类别,则程序格式错误。

  • 3-6) 仅用于说明的 as-variant 函数模板接受一个其类型可用于 `std::variant` 推导的值(即,`std::variant` 或派生自 `std::variant` 的类型),并返回具有相同 const-限定和值类别的 `std::variant` 值。
    • 3,4) 返回 `var`。
    • 5,6) 返回 `std::move(var)`。

参数

`vis` - 一个可调用对象,接受来自每个变体的所有可能替代项。`vars` - 要传递给访问器的变体列表。

返回值

  1. `INVOKE` 操作的结果。返回类型是通过将 decltype 应用于结果获得的类型。
  2. 如果 `R` 是(可能 cv-限定的)`void`,则无;否则为 `INVOKE` 操作的结果。
    3-6) 从 `var` 转换而来的 `std::variant` 值。

异常

如果 `vars` 中的任何变体 `vars_i` 的 `as-variant(vars_i).valueless_by_exception()` 为 `true`,则抛出 `std::bad_variant_access`。

复杂度

当变体数量为零或一个时,可调用对象的调用以*常数*时间实现,即它不依赖于变体中可以存储的类型数量。

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

备注

设 `n` 为 `(1 * ... * std::variant_size_v>)`,实现通常为 `std::visit` 的每个特化生成一个等同于 `n` 个函数指针(可能多维)数组的表,这类似于虚函数的实现。

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

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

|功能测试宏 |值| Std |注释| |::|::|::|::| |__cpp_lib_variant| 202102L|(C++17) (DR) |std::visit for classes derived from std::variant|

示例

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

// the variant to visit
using var_t = std::variant<int, long, double, std::string>;

// helper constant for the visitor #3
template<class>
inline constexpr bool always_false_v = false;

// helper type for the visitor #4
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of 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 visitor, only called for side-effects (here, for I/O)
std::visit([](auto&& arg){ std::cout << arg; }, v);

// 2. value-returning visitor, demonstrates the idiom of returning another variant
var_t w = std::visit([](auto&& arg) -> var_t { return arg + arg; }, v);

// 3. type-matching visitor: a lambda that handles each type differently
std::cout << ". After doubling, variant holds ";
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. another type-matching visitor: a class with 3 overloaded operator()'s
// Note: The `(auto arg)` template operator() will bind to `int` and `long`
// in this case, but in its absence the `(double arg)` operator()
// *will also* bind to `int` and `long` because both are implicitly
// convertible to double. When using this form, care has to be taken
// that implicit conversions are handled correctly.
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 2970C++17重载 (1) 的返回类型未保留 INVOKE 操作结果的值类别保留
LWG 3052 (P2162R2)C++17如果 `Variants` 中的任何类型不是 `std::variant`,则效果未指定已指定。

std::visit

定义于头文件 <variant>

声明

// 1)
template <class Visitor, class... Variants>
constexpr /* see below */ visit( Visitor&& vis, Variants&&... vars );
// 2)
template <class R, class Visitor, class... Variants>
constexpr R visit( Visitor&& vis, Variants&&... vars );
// 3)
template<class... Ts>
auto&& as-variant( std::variant<Ts...>& var ); // exposition only
// 4)
template<class... Ts>
auto&& as-variant( const std::variant<Ts...>& var ); // exposition only
// 5)
template<class... Ts>
auto&& as-variant( std::variant<Ts...>&& var ); // exposition only
// 6)
template<class... Ts>
auto&& as-variant( const std::variant<Ts...>&& var ); // exposition only

将访问器 `vis` (一个可调用对象,可以与来自变体的任何类型组合调用) 应用于变体 `vars`。

给定 `VariantBases` 为 decltype(as-variant(`std::forward(vars))... (一个包含 sizeof...(Variants) 个类型的包)

    1. 调用 `vis`,如同
INVOKE(std::forward<Visitor>(vis),
std::get<indices>(std::forward<VariantBases>(vars))...)

,其中 `indices` 是 `as-variant(vars).index()....`

    1. 调用 `vis`,如同
INVOKE<R>(std::forward<Visitor>(vis),
std::get<indices>(std::forward<VariantBases>(vars))...)

,其中 `indices` 是 `as-variant(vars).index()....`

只有当 `VariantBases` 中的每个类型都是有效类型时,这些重载才参与重载解析。如果 `INVOKE` 表达式(或 `INVOKE` (自 C++20 起))无效,或者 `INVOKE`(或 `INVOKE` (自 C++20 起))对于不同的索引具有不同的类型或值类别,则程序格式错误。

  • 3-6) 仅用于说明的 as-variant 函数模板接受一个其类型可用于 `std::variant` 推导的值(即,`std::variant` 或派生自 `std::variant` 的类型),并返回具有相同 const-限定和值类别的 `std::variant` 值。
    • 3,4) 返回 `var`。
    • 5,6) 返回 `std::move(var)`。

参数

`vis` - 一个可调用对象,接受来自每个变体的所有可能替代项。`vars` - 要传递给访问器的变体列表。

返回值

  1. `INVOKE` 操作的结果。返回类型是通过将 decltype 应用于结果获得的类型。
  2. 如果 `R` 是(可能 cv-限定的)`void`,则无;否则为 `INVOKE` 操作的结果。
    3-6) 从 `var` 转换而来的 `std::variant` 值。

异常

如果 `vars` 中的任何变体 `vars_i` 的 `as-variant(vars_i).valueless_by_exception()` 为 `true`,则抛出 `std::bad_variant_access`。

复杂度

当变体数量为零或一个时,可调用对象的调用以*常数*时间实现,即它不依赖于变体中可以存储的类型数量。

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

备注

设 `n` 为 `(1 * ... * std::variant_size_v>)`,实现通常为 `std::visit` 的每个特化生成一个等同于 `n` 个函数指针(可能多维)数组的表,这类似于虚函数的实现。

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

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

|功能测试宏 |值| Std |注释| |::|::|::|::| |__cpp_lib_variant| 202102L|(C++17) (DR) |std::visit for classes derived from std::variant|

示例

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

// the variant to visit
using var_t = std::variant<int, long, double, std::string>;

// helper constant for the visitor #3
template<class>
inline constexpr bool always_false_v = false;

// helper type for the visitor #4
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of 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 visitor, only called for side-effects (here, for I/O)
std::visit([](auto&& arg){ std::cout << arg; }, v);

// 2. value-returning visitor, demonstrates the idiom of returning another variant
var_t w = std::visit([](auto&& arg) -> var_t { return arg + arg; }, v);

// 3. type-matching visitor: a lambda that handles each type differently
std::cout << ". After doubling, variant holds ";
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. another type-matching visitor: a class with 3 overloaded operator()'s
// Note: The `(auto arg)` template operator() will bind to `int` and `long`
// in this case, but in its absence the `(double arg)` operator()
// *will also* bind to `int` and `long` because both are implicitly
// convertible to double. When using this form, care has to be taken
// that implicit conversions are handled correctly.
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 2970C++17重载 (1) 的返回类型未保留 INVOKE 操作结果的值类别保留
LWG 3052 (P2162R2)C++17如果 `Variants` 中的任何类型不是 `std::variant`,则效果未指定已指定。