Namespaces
Variants

std:: visit

From cppreference.net
Utilities library
Определено в заголовочном файле <variant>
template < class Visitor, class ... Variants >
constexpr /* смотрите ниже */ 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 с той же константной квалификацией и категорией значения.
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
(публичная функция-член)