Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/utility/variant/visit"

From cppreference.com
< cpp‎ | utility‎ | variant
(Example: rm always_false)
 
(34 intermediate revisions by 13 users not shown)
Line 2: Line 2:
 
{{cpp/utility/variant/navbar}}
 
{{cpp/utility/variant/navbar}}
 
{{dcl begin}}
 
{{dcl begin}}
{{dcl header | variant}}
+
{{dcl header|variant}}
{{dcl | since=c++17 |
+
{{dcl|since=c++17|num=1|
template <class Visitor, class... Variants>
+
template< class Visitor, class... Variants >
constexpr /*see below*/ visit(Visitor&& vis, Variants&&... vars);
+
constexpr /* see below */ visit( Visitor&& vis, Variants&&... vars );
 +
}}
 +
{{dcl|since=c++20|num=2|
 +
template< class R, class Visitor, class... Variants >
 +
constexpr R visit( Visitor&& vis, Variants&&... vars );
 +
}}
 +
{{dcl|notes={{mark expos}}|num=3|
 +
template< class... Ts >
 +
auto&& as-variant( std::variant<Ts...>& var );
 +
}}
 +
{{dcl|notes={{mark expos}}|num=4|
 +
template< class... Ts >
 +
auto&& as-variant( const std::variant<Ts...>& var );
 +
}}
 +
{{dcl|notes={{mark expos}}|num=5|
 +
template< class... Ts >
 +
auto&& as-variant( std::variant<Ts...>&& var );
 +
}}
 +
{{dcl|notes={{mark expos}}|num=6|
 +
template< class... Ts >
 +
auto&& as-variant( const std::variant<Ts...>&& var );
 
}}
 
}}
 
{{dcl end}}
 
{{dcl end}}
  
Applies the visitor {{tt|vis}} to the variants {{tt|vars}}
+
Applies the visitor {{c|vis}} (a {{named req|Callable}} that can be called with any combination of types from variants) to the variants {{c|vars}}.
  
Effectively returns
+
Given {{tt|VariantBases}} as {{c/core|decltype(}}{{tti|as-variant}}{{c/core|(std::forward<Variants>(vars))...}} (a pack of {{c|sizeof...(Variants)}} types):
 +
@1@ Invokes {{c|vis}} as if by
 +
{{box|{{lti|cpp/utility/functional|INVOKE}}{{c/core|(std::forward<Visitor>(vis),}}<br>{{c/core|      std::get<indices>(std::forward<VariantBases>(vars))...)}}}},
 +
where {{c|indices}} is {{box|{{tti|as-variant}}{{c/core|(vars).index()...}}}}.
  
{{cc|std::invoke(std::forward<Visitor>(vis), std::get<is>(std::forward<Variants>(vars))...)}}
+
@2@ Invokes {{c|vis}} as if by
 +
{{box|{{lti|cpp/utility/functional|INVOKE<R>}}{{c/core|(std::forward<Visitor>(vis),}}<br>{{c/core|          std::get<indices>(std::forward<VariantBases>(vars))...)}}}},
 +
where {{c|indices}} is {{box|{{tti|as-variant}}{{c/core|(vars).index()...}}}}.
  
, where {{tt|is...}} is {{tt|vars.index()...}}. The return type is deduced from the returned expression as if by {{c|decltype}}.
+
{{cpp/enable if|plural=yes|every type in {{tt|VariantBases}} is a valid type.}} If the expression denoted by {{lti|cpp/utility/functional|INVOKE}}{{rev inl|since=c++20| or {{lti|cpp/utility/functional|INVOKE<R>}}}} is invalid, or the results of {{lti|cpp/utility/functional|INVOKE}}{{rev inl|since=c++20| or {{lti|cpp/utility/functional|INVOKE<R>}}}} have different types or value categories for different {{c|indices}}, the program is ill-formed.
  
The call is ill-formed if the invocation above is not a valid expression of the same type and value category, for all combinations of alternative types of all variants.
+
@3-6@ The exposition-only {{tti|as-variant}} function templates accept a value whose type can be [[cpp/language/template argument deduction|deduced]] for {{c/core|std::variant<Ts...>}} (i.e. either {{c/core|std::variant<Ts...>}} or a type derived from {{c/core|std::variant<Ts...>}}), and return the {{lc|std::variant}} value with the same const-qualification and value category.
 +
:@3,4@ Returns {{c|var}}.
 +
:@5,6@ Returns {{c|std::move(var)}}.
  
 
===Parameters===
 
===Parameters===
 
{{par begin}}
 
{{par begin}}
{{par | vis | a {{named req|Callable}} that accepts every possible alternative from every variant}}
+
{{par|vis|a {{named req|Callable}} that accepts every possible alternative from every variant}}
{{par | vars | list of variants to pass to the visitor }}
+
{{par|vars|list of variants to pass to the visitor}}
 
{{par end}}
 
{{par end}}
  
 
===Return value===
 
===Return value===
The value returned by the selected invocation of the visitor.
+
@1@ The result of the {{lti|cpp/utility/functional|INVOKE}} operation. The return type is the type obtained from applying {{ltt|cpp/language/decltype}} to the result.
 +
@2@ Nothing if {{tt|R}} is (possibly cv-qualified) {{c/core|void}}; otherwise the result of the {{lti|cpp/utility/functional|INVOKE<R>}} operation.
 +
@3-6@ A {{lc|std::variant}} value converted from {{c|var}}.
  
 
===Exceptions===
 
===Exceptions===
Throws {{lc|std::bad_variant_access}} if any variant in {{tt|vars}} is {{ltt|cpp/utility/variant/valueless_by_exception|valueless_by_exception}}.
+
Throws {{lc|std::bad_variant_access}} if {{box|{{tti|as-variant}}{{c/core|(vars_i).valueless_by_exception()}}}} is {{c|true}} for any variant {{c|vars_i}} in {{c|vars}}.
  
 
===Complexity===
 
===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 {{tt|sizeof...(Types)}}.
+
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 1, the invocation of the callable object has no complexity requirements.
+
If the number of variants is larger than one, the invocation of the callable object has no complexity requirements.
 +
 
 +
===Notes===
 +
Let {{c|n}} be {{c|(1 * ... * std::variant_size_v<std::remove_reference_t<VariantBases>>)}}, implementations usually generate a table equivalent to an (possibly multidimensional) array of {{c|n}} function pointers for every specialization of {{tt|std::visit}}, which is similar to the implementation of [[cpp/language/virtual|virtual functions]].
 +
 
 +
Implementations may also generate a [[cpp/language/switch|switch statement]] with {{c|n}} branches for {{tt|std::visit}} (e.g. the MSVC STL implementation uses a switch statement when {{c|n}} is not greater than 256).
 +
 
 +
On typical implementations, the time complexity of the invocation of {{c|vis}} can be considered equal to that of access to an element in an (possibly multidimensional) array or execution of a switch statement.
 +
 
 +
{{ftm begin|std=yes|comment=yes}}
 +
{{ftm|std=C++17|dr=yes|value=202102L|__cpp_lib_variant|{{tt|std::visit}} for classes derived from {{lc|std::variant}}}}
 +
{{ftm end}}
  
 
===Example===
 
===Example===
 
{{example
 
{{example
| code=
+
|code=
 
#include <iomanip>
 
#include <iomanip>
 
#include <iostream>
 
#include <iostream>
Line 46: Line 86:
 
#include <vector>
 
#include <vector>
  
 
+
// the variant to visit
template<class T> struct always_false : std::false_type {};
+
 
+
 
using var_t = std::variant<int, long, double, std::string>;
 
using var_t = std::variant<int, long, double, std::string>;
  
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
+
// helper type for the visitor #4
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
+
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() {
+
int main()
 +
{
 
     std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
 
     std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
 
+
      
     // type-matching visitor: can also be a class with 4 overloaded operator()'s
+
     for (auto& v: vec)
    auto visitor = [](auto&& arg) {
+
    {
        using T = std::decay_t<decltype(arg)>;
+
         // 1. void visitor, only called for side-effects (here, for I/O)
        if constexpr (std::is_same_v<T, int>)
+
         std::visit([](auto&& arg){ std::cout << arg; }, v);
            std::cout << "int with value " << arg << '\n';
+
       
        else if constexpr (std::is_same_v<T, long>)
+
         // 2. value-returning visitor, demonstrates the idiom of returning another variant
            std::cout << "long with value " << arg << '\n';
+
         var_t w = std::visit([](auto&& arg) -> var_t { return arg + arg; }, v);
        else if constexpr (std::is_same_v<T, double>)
+
       
            std::cout << "double with value " << arg << '\n';
+
        // 3. type-matching visitor: a lambda that handles each type differently
        else if constexpr (std::is_same_v<T, std::string>)
+
            std::cout << "std::string with value " << std::quoted(arg) << '\n';
+
        else
+
            static_assert(always_false<T>::value, "non-exhaustive visitor!");
+
    };
+
 
+
     for(auto& v: vec) {
+
         // void visitor, only called for side-effects
+
         std::visit([](auto&& arg){std::cout << arg;}, v);
+
 
+
         // value-returning visitor. A common idiom is to return another variant
+
         var_t w = std::visit([](auto&& arg) -> var_t {return arg + arg;}, v);
+
 
+
 
         std::cout << ". After doubling, variant holds ";
 
         std::cout << ". After doubling, variant holds ";
         std::visit(visitor, w);
+
         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) {
+
     for (auto& v: vec)
         std::visit(overloaded {
+
    {
 +
        // 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 << ' '; },
 
             [](auto arg) { std::cout << arg << ' '; },
 
             [](double arg) { std::cout << std::fixed << arg << ' '; },
 
             [](double arg) { std::cout << std::fixed << arg << ' '; },
             [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
+
             [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
 
         }, v);
 
         }, v);
 
     }
 
     }
 
}
 
}
| output=
+
|output=
 
10. After doubling, variant holds int with value 20
 
10. After doubling, variant holds int with value 20
 
15. After doubling, variant holds long with value 30
 
15. After doubling, variant holds long with value 30
Line 98: Line 148:
 
10 15 1.500000 "hello"  
 
10 15 1.500000 "hello"  
 
}}
 
}}
 +
 +
===Defect reports===
 +
{{dr list begin}}
 +
{{dr list item|wg=lwg|dr=2970|std=C++17|before=the return type of overload {{v|1}} did not preserve the<br>value category of the result of the {{tti|INVOKE}} operation|after=preserves}}
 +
{{dr list item|wg=lwg|dr=3052|paper=P2162R2|std=C++17|before=the effects were unspecified if any type<br>in {{tt|Variants}} is not a {{lc|std::variant}}|after=specified}}
 +
{{dr list end}}
  
 
===See also===
 
===See also===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc inc | cpp/utility/variant/dsc swap}}
+
{{dsc inc|cpp/utility/variant/dsc swap}}
 
{{dsc end}}
 
{{dsc end}}
  
{{langlinks|ja|zh}}
+
{{langlinks|de|es|ja|ru|zh}}

Latest revision as of 04:07, 21 April 2024

 
 
Utilities library
General utilities
Relational operators (deprecated in C++20)
 
 
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*)

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).

Contents

[edit] Parameters

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

[edit] 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.

[edit] Exceptions

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

[edit] 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.

[edit] 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)
(DR)
std::visit for classes derived from std::variant

[edit] Example

#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"

[edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

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

[edit] See also

swaps with another variant
(public member function) [edit]