名前空間
変種
操作

std::visit

From cppreference.com
< cpp‎ | utility‎ | variant
 
 
ユーティリティライブラリ
言語サポート
型のサポート (基本型、RTTI)
ライブラリ機能検査マクロ (C++20)
プログラムユーティリティ
可変引数関数
コルーチンサポート (C++20)
契約サポート (C++26)
三方比較
(C++20)
(C++20)(C++20)(C++20)  
(C++20)(C++20)(C++20)

汎用ユーティリティ
関係演算子 (C++20で非推奨)
 
 
ヘッダ <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) (説明用*)

Visitor (Variantsの型からのあらゆる組み合わせで呼び出し可能なCallable) v を、Variants の値 values に適用します。

VariantBasesdecltype(as-variant(std::forward<Variants>(values))... ( sizeof...(Variants) 個の型のパック) として、

1) v を次のように呼び出します。

INVOKE(std::forward<Visitor>(v),
       std::get<indices>(std::forward<VariantBases>(values))...)
,

ここで indicesas-variant(values).index()... です。
2) v を次のように呼び出します。

INVOKE<R>(std::forward<Visitor>(v),
          std::get<indices>(std::forward<VariantBases>(values))...)
,

ここで indicesas-variant(values).index()... です。

これらのオーバーロードは、VariantBases のすべての型が有効な型である場合にのみ、オーバーロード解決に参加します。 INVOKE または INVOKE<R>(C++20以降) で示される式が無効である場合、または indices の型や値カテゴリが異なる場合、プログラムは不正形成となります。

3-6) 露出専用の as-variant 関数テンプレートは、std::variant<Ts...> のために型を推論できる値(つまり、std::variant<Ts...> または std::variant<Ts...> から派生した型)を受け取り、同じ const 修飾と値カテゴリを持つ std::variant 値を返します。
3,4) value を返します。
5,6) std::move(value) を返します。

目次

[編集] パラメータ

v - Variants のすべてのバリアントのすべての可能な代替を受け入れる Callable
values - ビジターに渡すバリアントのリスト

[編集] 戻り値

1) INVOKE 操作の結果。返り値の型は、結果に decltype を適用して得られる型です。
2) R が (cv 修飾されている可能性のある) void の場合はなし。それ以外の場合は、INVOKE<R> 操作の結果。
3-6) value から変換された std::variant 値。

[編集] 例外

as-variant(value_i).valueless_by_exception() が、values 内の任意のバリアント value_i に対して true の場合、std::bad_variant_access をスローします。

[編集] 計算量

バリアントの数が 0 または 1 の場合、Callable オブジェクトの呼び出しは定数時間で実装されます。つまり、variant に格納できる型数に依存しません。

バリアントの数が 1 より大きい場合、Callable オブジェクトの呼び出しには計算量要件はありません。

[編集] 注記

n(1 * ... * std::variant_size_v<std::remove_reference_t<VariantBases>>) とすると、実装では通常、std::visit のすべての特殊化に対して、(多次元の可能性もある) 関数ポインタの配列に相当するテーブルを生成します。これは 仮想関数 の実装に似ています。

実装では、std::visit のために n 個の分岐を持つ switch文 を生成する場合もあります (例: MSVC STL の実装では n が 256 以下の場合に switch文 を使用します)。

通常の И実装では、v の呼び出しの計算量は、(多次元の可能性もある) 配列の要素へのアクセスや switch文 の実行の計算量と同等と見なすことができます。

機能テストマクロ 規格 機能
__cpp_lib_variant 202102L (C++23)
(DR17)
std::variant から派生したクラスに対する std::visit

[編集]

#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
 
// the variant to visit
using value_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<value_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
        value_t w = std::visit([](auto&& arg) -> value_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);
    }
}

出力

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"

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
LWG 2970 C++17 オーバーロード (1) の返り値の型が、INVOKE 操作の結果の
値カテゴリを保持していませんでした。
保持
LWG 3052
(P2162R2)
C++17 Variants 内のいずれかの型が std::variant でない場合の
動作が未指定でした。
指定された

[編集] 関連項目

(C++26)
variant が保持する引数で、与えられたファンクタを呼び出す
(public member function) [編集]
別の variant と交換する
(public member function) [編集]
English 日本語 中文(简体) 中文(繁體)