Namespaces
Variants

std::visit

С сайта ru.cppreference.net
 
 
Библиотека утилит
Поддержка языка
Поддержка типов (базовые типы, RTTI)
Макросы для проверки возможностей библиотеки (C++20)
Программные утилиты
Функции с переменным числом аргументов
Поддержка корутин (C++20)
Поддержка контрактов (C++26)
Трёхстороннее сравнение
(C++20)
(C++20)(C++20)(C++20)    
(C++20)(C++20)(C++20)

Общие утилиты
Операторы отношения (устарело в C++20)
 
 
Определён в заголовке <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) типов):

1) Вызывает v как если бы с помощью

INVOKE(std::forward<Visitor>(v),
std::get<indices>(std::forward<VariantBases>(values))...)
, где indices равно as-variant(values).index()....

2) Вызывает 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, программа считается некорректной.

3-6) Шаблоны функций, предназначенные только для демонстрацииas-variant, принимают значение, чей тип может бытьвыведен для std::variant<Ts...> (т.е. либо std::variant<Ts...>, либо тип, производный от std::variant<Ts...>), и возвращают std::variant значение с той же cv-квалификацией и категорией значения.
3,4) Возвращает value.
5,6) Возвращает std::move(value).

Параметры

v - a Callable который принимает любую возможную альтернативу из каждого варианта в Variants
values - список вариантов для передачи посетителю

Возвращаемое значение

1) Результат операции INVOKE . Тип возвращаемого значения - это тип, полученный применением decltype к результату.
2) Ничего, если R является (возможно, с cv-квалификаторами) void ; иначе результат операции INVOKE<R> .
3-6) Значение std::variant преобразованное из value .

Исключения

Выбрасывает 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
(публичная функция-член)