std::visit(3)
πΈ std::visit π (from the C++ Standard Library manual, bestie)
okay so this is the std::visit(3) page from the C++ Standard Library section, and honestly? it's giving "the friend who introduces everyone at the party" energy π₯. Let's get into it.
β¨ NAME β¨
std::visit β literally just std::visit, that's the whole name, no notes π
π¦ SYNOPSIS π¦
Defined in header <variant>:
template< class Visitor, class... Variants >
constexpr /* see below */ visit( Visitor&& vis, Variants&&... vars );
(1) (since C++17)
template< class R, class Visitor, class... Variants >
constexpr R visit( Visitor&& vis, Variants&&... vars );
(2) (since C++20)
template< class... Ts >
auto&& as-variant( std::variant<Ts...>& var );
(3) (exposition only*)
template< class... Ts >
auto&& as-variant( const std::variant<Ts...>& var );
(4) (exposition only*)
template< class... Ts >
auto&& as-variant( std::variant<Ts...>&& var );
(5) (exposition only*)
template< class... Ts >
auto&& as-variant( const std::variant<Ts...>&& var );
(6) (exposition only*)
π DESCRIPTION π
So what does it actually do? It applies the visitor vis β a Callable that can be called with any combination of types from variants β to the variants vars. that's it. that's the vibe. π«Ά
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 inVariantBasesis a valid type. If the expression denoted byINVOKEorINVOKE<R>(since C++20) is invalid, or the results ofINVOKEorINVOKE<R>(since C++20) have different types or value categories for different indices, the program is ill-formed. no cap, that's a hard fail, bestie π
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. iconic behaviour honestly:
- 3,4) Returns
var. - 5,6) Returns
std::move(var).
π PARAMETERS π
visβ a Callable that accepts every possible alternative from every variant. yes, all of them. it's an overachiever π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. slay, that's so clean β¨
If the number of variants is larger than one, the invocation of the callable object has no complexity requirements. free real estate, do whatever π
π§ 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. iykyk π
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 situation β¨:
__cpp_lib_variantβ value202102Lβ Std(C++17)β Feature:std::visitfor classes derived fromstd::variant(DR)
π EXAMPLE π
(the original page has a lil // Run this code thingy right here π)
#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. (yes, they went back and fixed stuff, we love accountability π)
LWG 2970β Applied toC++17β Behavior as published: the return type of overload (1) did not preserve the value category of the result of theINVOKEoperation β Correct behavior: preservesLWG 3052(P2162R2) β Applied toC++17β Behavior as published: the effects were unspecified if any type inVariantsis not astd::variantβ Correct behavior: specified
π₯ SEE ALSO π₯
swapβ swaps with another variant (public member function)
and that's the whole page bestie, you ate that π«Ά
sign-off time π β this lil masterpiece comes to you from http://cppreference.com, dated 2024.06.10, and it's the std::visit(3) page, mwah π
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms