Пространства имён
Варианты
Действия

std::visit

Материал из cppreference.com
 
 
Библиотека утилит
Языковая поддержка
Поддержка типов (базовые типы, RTTI)
Макросы тестирования функциональности библиотеки (C++20)    
Управление динамической памятью
Программные утилиты
Поддержка сопрограмм (C++20)
Вариативные функции
Трёхстороннее сравнение (C++20)
(C++20)
(C++20)(C++20)(C++20)(C++20)(C++20)(C++20)
Общие утилиты
Дата и время
Функциональные объекты
Библиотека форматирования (C++20)
(C++11)
Операторы отношения (устарело в C++20)
Целочисленные функции сравнения
(C++20)(C++20)(C++20)    
(C++20)
Операции обмена и типа
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
Общие лексические типы
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
Элементарные преобразования строк
(C++17)
(C++17)
 
 
<tbody> </tbody>
Определено в заголовочном файле <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)):

1) Вызывает vis как будто с помощью INVOKE(std::forward<Visitor>(vis),
std::get<indices>(std::forward<VariantBases>(vars))...)
, где indices равно as-variant(vars).index()....
2) Вызывает 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, программа некорректная.

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

Параметры

vis — Callable, который принимает все возможные альтернативы из каждого variant
vars — список variant'ов для передачи посетителю

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

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