std::visit
| Определён в заголовке <variant>
|
||
template< class Visitor, class... Variants >
constexpr /* see below */ 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 (вызываемый объектCallable, который можно вызывать с любой комбинацией типов из Variants) к Variants values.
Пусть VariantBases обозначает decltype(as-variant(std::forward<Variants>(values))... (набор из sizeof...(Variants) типов):
v как если бы с помощью
INVOKE(std::forward<Visitor>(v),std::get<indices>(std::forward<VariantBases>(values))...),
где indices равно as-variant(values).index()....
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, программа считается некорректной.
as-variant, принимают значение, чей тип может бытьвыведен для std::variant<Ts...> (т.е. либо std::variant<Ts...>, либо тип, производный от std::variant<Ts...>), и возвращают std::variant значение с той же cv-квалификацией и категорией значения.value.std::move(value).Параметры
| v | - | a Callable который принимает любую возможную альтернативу из каждого варианта в Variants |
| values | - | список вариантов для передачи посетителю |
Возвращаемое значение
R
является (возможно, с cv-квалификаторами)
void
; иначе результат операции
INVOKE<R>
.
Исключения
Выбрасывает
std::bad_variant_access
если
as-variant
(
value_i
)
.
valueless_by_exception
(
)
равно
true
для любого варианта
value_i
в
values
.
Сложность
Когда количество вариантов равно нулю или одному, вызов вызываемого объекта реализуется за постоянное время; т.е. оно не зависит от количества типов, которые могут храниться в варианте.
Если количество вариантов больше одного, вызов вызываемого объекта не имеет требований к сложности.
Примечания
Пусть
n
будет
(
1
*
...
*
std::
variant_size_v
<
std::
remove_reference_t
<
VariantBases
>>
)
, реализации обычно генерируют таблицу, эквивалентную (возможно, многомерному) массиву из
n
указателей на функции для каждой специализации
std::visit
, что аналогично реализации
виртуальных функций
.
Реализации также могут генерировать
switch statement
с
n
ветвями для
std::visit
(например, реализация MSVC STL использует switch statement, когда
n
не превышает 256).
В типичных реализациях временная сложность вызова 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> // вариант для посещения 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, "привет"}; for (auto& v: vec) { // 1. void visitor, вызывается только для side-effects (здесь, для I/O) std::visit([](auto&& arg){ std::cout << arg; }, v); // 2. возвращающий значение посетитель, демонстрирует идиому возврата другого варианта value_t w = std::visit([](auto&& arg) -> value_t { return arg + arg; }, v); // 3. посетитель с сопоставлением типов: лямбда, которая обрабатывает каждый тип по-разному std::cout << ". После удвоения вариант содержит "; 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 (Примечание: Согласно требованиям, HTML-теги и атрибуты не переведены, термин `std::quoted` как специфический для C++ оставлен без перевода)(arg) << '\n'; else static_assert(false, "неполный посетитель!"); }, w); } for (auto& v: vec) { // 4. другой тип-матчинговый посетитель: класс с 3 перегруженными operator() // Примечание: Шаблонный оператор `(auto arg)` будет привязываться к `int` и `long` // в данном случае, но при его отсутствии оператор `(double arg)` operator() // *также* будет связываться с `int` и `long`, поскольку оба неявно // convertible to double. При использовании этой формы необходимо соблюдать осторожность // что неявные преобразования обрабатываются корректно. std::visit(overloaded{ [](auto arg) { std::cout << arg << ' '; }, [](double arg) { std::cout << std::fixed << arg << ' '; }, [](const std::string& arg) { std::cout << std::quoted (Примечание: Согласно требованиям, HTML-теги и атрибуты не переведены, термин `std::quoted` как специфический для C++ оставлен без перевода)(arg) << ' '; } }, v); } }
Вывод:
10. После удвоения, variant содержит int со значением 20 15. После удвоения, variant содержит long со значением 30 1.5. После удвоения, variant содержит double со значением 3 hello. После удвоения, variant содержит std::string со значением "hellohello" 10 15 1.500000 "hello"
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| LWG 2970 | C++17 |
the return type of overload
(1)
did not preserve the
value category of the result of the
INVOKE
operation
|
preserves |
|
LWG 3052
( P2162R2 ) |
C++17 |
the effects were unspecified if any type
in
Variants
is not a
std::variant
|
specified |
Смотрите также
|
(C++26)
|
вызывает предоставленный функтор с аргументом, содержащимся в
variant
(публичная функция-член) |
обменивается содержимым с другим
variant
(публичная функция-член) |