std::visit
定义于头文件 <variant>
。
声明
- C++20
- C++17
// 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
// 1)
template <class Visitor, class... Variants>
constexpr /* see below */ 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
-
- 调用 `vis`,如同
INVOKE(std::forward<Visitor>(vis),
std::get<indices>(std::forward<VariantBases>(vars))...)
,其中 `indices` 是 `as-variant(vars).index()....`
-
- 调用 `vis`,如同
INVOKE<R>(std::forward<Visitor>(vis),
std::get<indices>(std::forward<VariantBases>(vars))...)
,其中 `indices` 是 `as-variant(vars).index()....`
只有当 `VariantBases` 中的每个类型都是有效类型时,这些重载才参与重载解析。如果 `INVOKE` 表达式(或 `INVOKE
- 3-6) 仅用于说明的 as-variant 函数模板接受一个其类型可用于 `std::variant
` 推导的值(即,`std::variant ` 或派生自 `std::variant ` 的类型),并返回具有相同 const-限定和值类别的 `std::variant` 值。 - 3,4) 返回 `var`。
- 5,6) 返回 `std::move(var)`。
参数
`vis` - 一个可调用对象,接受来自每个变体的所有可能替代项。`vars` - 要传递给访问器的变体列表。
返回值
- `INVOKE` 操作的结果。返回类型是通过将 decltype 应用于结果获得的类型。
- 如果 `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` 个分支的 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 2970 | C++17 | 重载 (1) 的返回类型未保留 INVOKE 操作结果的值类别 | 保留 |
LWG 3052 (P2162R2) | C++17 | 如果 `Variants` 中的任何类型不是 `std::variant`,则效果未指定 | 已指定。 |