std::visit(3)

NAME
std::visit - std::visit

Synopsis
   Defined in header <variant>
   template< class Visitor, class... Variants >
   constexpr /* see below */ visit( Visitor&& vis, Variants&&... (1) (since C++17)
   vars );
   template< class R, class Visitor, class... Variants > (2) (since C++20)
   constexpr R visit( Visitor&& vis, Variants&&... vars );
   template< class... Ts > (3) (exposition only*)
   auto&& as-variant( std::variant<Ts...>& var );
   template< class... Ts > (4) (exposition only*)
   auto&& as-variant( const std::variant<Ts...>& var );
   template< class... Ts > (5) (exposition only*)
   auto&& as-variant( std::variant<Ts...>&& var );
   template< class... Ts > (6) (exposition only*)
   auto&& as-variant( const std::variant<Ts...>&& var );
   Applies the visitor vis (a Callable that can be called with any combination of types
   from variants) to the variants vars.
   Given VariantBases as decltype(as-variant(std::forward<Variants>(vars))... (a pack
   of sizeof...(Variants) types):
   1) Invokes vis as if by
   INVOKE(std::forward<Visitor>(vis),
          std::get<indices>(std::forward<VariantBases>(vars))...),
   where indices is as-variant(vars).index()....
   2) Invokes vis as if by
   INVOKE<R>(std::forward<Visitor>(vis),
             std::get<indices>(std::forward<VariantBases>(vars))...),
   where indices is as-variant(vars).index()....
   These overloads participate in overload resolution only if every type in
   VariantBases is a valid type. If the expression denoted by INVOKE
   or INVOKE<R>
   (since C++20) is invalid, or the results of INVOKE
   or INVOKE<R>
   (since C++20) have different types or value categories for different indices, the
   program is ill-formed.
   3-6) The exposition-only as-variant function templates accept a value whose type can
   be deduced for std::variant<Ts...> (i.e. either std::variant<Ts...> or a type
   derived from std::variant<Ts...>), and return the std::variant value with the same
   const-qualification and value category.
   3,4) Returns var.
   5,6) Returns std::move(var).

Parameters
   vis - a Callable that accepts every possible alternative from every variant
   vars - list of variants to pass to the visitor

Return value
   1) The result of the INVOKE operation. The return type is the type obtained from
   applying decltype to the result.
   2) Nothing if R is (possibly cv-qualified) void; otherwise the result of the
   INVOKE<R> operation.
   3-6) A std::variant value converted from var.

Exceptions
   Throws std::bad_variant_access if as-variant(vars_i).valueless_by_exception() is
   true for any variant vars_i in vars.

Complexity
   When the number of variants is zero or one, the invocation of the callable object is
   implemented in constant time, i.e. it does not depend on the number of types can be
   stored in the variant.
   If the number of variants is larger than one, the invocation of the callable object
   has no complexity requirements.

Notes
   Let n be (1 * ... * std::variant_size_v<std::remove_reference_t<VariantBases>>),
   implementations usually generate a table equivalent to an (possibly
   multidimensional) array of n function pointers for every specialization of
   std::visit, which is similar to the implementation of virtual functions.
   Implementations may also generate a switch statement with n branches for std::visit
   (e.g. the MSVC STL implementation uses a switch statement when n is not greater than
   256).
   On typical implementations, the time complexity of the invocation of vis can be
   considered equal to that of access to an element in an (possibly multidimensional)
   array or execution of a switch statement.
   Feature-test macro Value Std Feature
   __cpp_lib_variant 202102L (C++17) std::visit for classes derived from std::variant
                        (DR)

Example
// Run this code
 #include <iomanip>
 #include <iostream>
 #include <string>
 #include <type_traits>
 #include <variant>
 #include <vector>
 // the variant to visit
 using var_t = std::variant<int, long, double, std::string>;
 // helper type for the visitor #4
 template<class... Ts>
 struct overloaded : Ts... { using Ts::operator()...; };
 // explicit deduction guide (not needed as of C++20)
 template<class... Ts>
 overloaded(Ts...) -> overloaded<Ts...>;
 int main()
 {
     std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
     for (auto& v: vec)
     {
         // 1. void visitor, only called for side-effects (here, for I/O)
         std::visit([](auto&& arg){ std::cout << arg; }, v);
         // 2. value-returning visitor, demonstrates the idiom of returning another variant
         var_t w = std::visit([](auto&& arg) -> var_t { return arg + arg; }, v);
         // 3. type-matching visitor: a lambda that handles each type differently
         std::cout << ". After doubling, variant holds ";
         std::visit([](auto&& arg)
         {
             using T = std::decay_t<decltype(arg)>;
             if constexpr (std::is_same_v<T, int>)
                 std::cout << "int with value " << arg << '\n';
             else if constexpr (std::is_same_v<T, long>)
                 std::cout << "long with value " << arg << '\n';
             else if constexpr (std::is_same_v<T, double>)
                 std::cout << "double with value " << arg << '\n';
             else if constexpr (std::is_same_v<T, std::string>)
                 std::cout << "std::string with value " << std::quoted(arg) << '\n';
             else
                 static_assert(false, "non-exhaustive visitor!");
         }, w);
     }
     for (auto& v: vec)
     {
         // 4. another type-matching visitor: a class with 3 overloaded operator()'s
         // Note: The `(auto arg)` template operator() will bind to `int` and `long`
         // in this case, but in its absence the `(double arg)` operator()
         // *will also* bind to `int` and `long` because both are implicitly
         // convertible to double. When using this form, care has to be taken
         // that implicit conversions are handled correctly.
         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);
     }
 }

Output:
 10. After doubling, variant holds int with value 20
 15. After doubling, variant holds long with value 30
 1.5. After doubling, variant holds double with value 3
 hello. After doubling, variant holds std::string with value "hellohello"
 10 15 1.500000 "hello"
   Defect reports
   The following behavior-changing defect reports were applied retroactively to
   previously published C++ standards.
      DR Applied to Behavior as published Correct behavior
                        the return type of overload (1) did not
   LWG 2970 C++17 preserve the preserves
                        value category of the result of the INVOKE
                        operation
   LWG 3052 C++17 the effects were unspecified if any type specified
   (P2162R2) in Variants is not a std::variant

See also
   swap swaps with another variant
        (public member function)