std::visit
| Определено в заголовочном файле <variant>
|
||
template <class Visitor, class... Variants> constexpr /* смотрите ниже */ 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) |
template<class... Ts> auto&& as-variant( std::variant<Ts...>& var ); |
(3) | (только для пояснения*) |
template<class... Ts> auto&& as-variant( const std::variant<Ts...>& var ); |
(4) | (только для пояснения*) |
template<class... Ts> auto&& as-variant( std::variant<Ts...>&& var ); |
(5) | (только для пояснения*) |
template<class... Ts> auto&& as-variant( const std::variant<Ts...>&& var ); |
(6) | (только для пояснения*) |
Применяет посетителя vis (Callable, который можно вызывать с любой комбинацией типов из variant'ов) к variant'ам vars.
Учитывая VariantBases как decltype(as-variant(std::forward<Variants>(vars))... (набор типов sizeof...(Variants)):
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<R> (начиная с C++20) недействительно, или результаты INVOKE или INVOKE<R> (начиная с C++20) имеют разные типы или категории значений для разных indices, программа некорректная.
as-variant, предназначенные только для описания, принимают значение, тип которого может быть выведен для std::variant<Ts...> (то есть либо std::variant<Ts...>, либо тип, производный от std::variant<Ts...>), и возвращают значение std::variant с той же const-квалификацией и категорией значений.var.std::move(var).Параметры
| vis | — | Callable, который принимает все возможные альтернативы из каждого variant |
| vars | — | список variant'ов для передачи посетителю |
Возвращаемое значение
var.Исключения
Генерирует std::bad_variant_access, если as-variant(vars_i).valueless_by_exception() равно true для любого variant vars_i в vars.
Сложность
Когда количество variant'ов равно нулю или одному, вызов вызываемого объекта осуществляется за константное время, т.е. не зависит от того, сколько типов может быть сохранено в variant.
Если количество variant'ов больше одного, вызов вызываемого объекта не требует сложности.
Примечание
Пусть n будет (1 * ... * std::variant_size_v<std::remove_reference_t<VariantBases>>), реализации обычно генерируют таблицу, эквивалентную (возможно, многомерному) массиву указателей на функции n для каждой специализации std::visit,что аналогично реализации виртуальных функций.
Реализации также могут генерировать оператор switch с n ветвями для std::visit (например, реализация MSVC STL использует оператор switch, когда n не больше 256).
В типичных реализациях временная сложность вызова vis может считаться равной сложности доступа к элементу в (возможно, многомерном) массиве или выполнению оператора switch.
| Макрос тест функциональности | Значение | Стандарт | Комментарий |
|---|---|---|---|
__cpp_lib_variant |
202102L |
(C++17) | std::visit для классов, производных от std::variant
|
Пример
#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
// variant для visit
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, "привет"};
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);
// 3. посетитель, соответствующий типу: лямбда, которая обрабатывает
// каждый тип по-разному
std::cout << ". После удвоения variant хранит ";
std::visit([](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>)
std::cout << "int с значением " << arg << '\n';
else if constexpr (std::is_same_v<T, long>)
std::cout << "long с значением " << arg << '\n';
else if constexpr (std::is_same_v<T, double>)
std::cout << "double с значением " << arg << '\n';
else if constexpr (std::is_same_v<T, std::string>)
std::cout << "std::string с значением " << std::quoted(arg) << '\n';
else
static_assert(always_false_v<T>, "неисчерпывающий посетитель!");
}, 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 хранит int с значением 20
15. После удвоения variant хранит long с значением 30
1.5. После удвоения variant хранит double с значением 3
привет. После удвоения variant хранит std::string с значением "приветпривет"
10 15 1.500000 "привет"
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| LWG 2970 | C++17 | возвращаемый тип перегрузки (1) не сохранял категорию значения результата операции INVOKE
|
сохраняет |
| LWG 3052 | C++17 | эффекты не были указаны, если какой-либо тип в Variants не являлся std::variant
|
указаны |
Смотрите также
| обменивает содержимое с другим variant (public функция-элемент) |