最終更新日時(UTC):
が更新

履歴 編集

function
<variant>

std::variant::コンストラクタ(C++17)

constexpr variant() noexcept(see below);                 // (1)
constexpr variant(const variant& other);                 // (2)
constexpr variant(variant&& other) noexcept(see below);  // (3)

template <class T>
constexpr variant(T&& t) noexcept(see below);            // (4)

template <class T, class... Args>
constexpr explicit variant(in_place_type_t<T>,
                           Args&&... args);              // (5)

template <class T, class U, class... Args>
constexpr explicit variant(in_place_type_t<T>,
                           initializer_list<U> il,
                           Args&&... args);              // (6)

template <size_t I, class... Args>
constexpr explicit variant(in_place_index_t<I>,
                           Args&&... args);              // (7)

template <size_t I, class U, class... Args>
constexpr explicit variant(in_place_index_t<I>,
                           initializer_list<U> il,
                           Args&&... args);              // (8)

template <class Alloc>
variant(allocator_arg_t,
        const Alloc& a);                                 // (9)

template <class Alloc>
variant(allocator_arg_t,
        const Alloc& a,
        const variant& other);                           // (10)

template <class Alloc>
variant(allocator_arg_t,
        const Alloc& a,
        variant&& other);                                // (11)

template <class Alloc, class T>
variant(allocator_arg_t,
        const Alloc& a,
        T&& x);                                          // (12)

template <class Alloc, class T, class... Args>
variant(allocator_arg_t,
        const Alloc& a,
        in_place_type_t<T> il,
        Args&&... args);                                 // (13)

template <class Alloc, class T, class U, class... Args>
variant(allocator_arg_t,
        const Alloc& a,
        in_place_type_t<T>,
        initializer_list<U> il,
        Args&&... args);                                 // (14)

template <class Alloc, size_t I, class... Args>
variant(allocator_arg_t,
       const Alloc& a,
       in_place_index_t<I>,
       Args&&... args);                                  // (15)

template <class Alloc, size_t I, class U, class... Args>
variant(allocator_arg_t,
        const Alloc& a,
        in_place_index_t<I>,
        initializer_list<U> il,
        Args&&... args);                                 // (16)

概要

variantオブジェクトを構築する。

  • (1) : デフォルト構築。0番目の候補型の値を保持する
  • (2) : コピーコンストラクタ
  • (3) : ムーブコンストラクタ
  • (4) : 候補型のいずれかの型の値を保持する
  • (5) : 候補型のうち、指定した型のコンストラクタ引数を受け取ってコンストラクタ内でそのオブジェクトを構築して保持する
  • (6) : 候補型のうち、指定した型のコンストラクタ引数ilargs...を受け取ってコンストラクタ内でそのオブジェクトを構築して保持する
  • (7) : 候補型のうち、指定したインデックスの型のコンストラクタ引数を受け取ってコンストラクタ内でそのオブジェクトを構築して保持する
  • (8) : 候補型のうち、指定したインデックスの型のコンストラクタ引数ilargs...を受け取ってコンストラクタ内でそのオブジェクトを構築して保持する
  • (9)-(16) : (1)-(8)のuses-allocator構築版

テンプレートパラメータ制約

  • 候補型Types...のi番目の型をTiとする
  • (1) :
  • (2) :
  • (3) :
  • (4) :
    • C++17 : ここで説明用に、*thisが保持している型Tjと、そのインデックス値jを定義する。Types...の各型Tiに対して擬似的な関数FUN(Ti)を定義したとして、FUN(std::forward<T>(t))呼び出しによって選択されたオーバーロードされた関数のパラメータ型を、構築してその後含まれる値の型をTjとする
    • C++20 : ここで説明用に、*thisが保持している型Tjと、そのインデックス値jを定義する。Types...の各型Tiを、縮小変換を受け付けない型であり (Ti x[] = {std::forward<T>(t)};)、CV修飾付きboolの場合にCV修飾を外したbool型になるとして、その型に対して擬似的な関数FUN(Ti)を定義したとして、FUN(std::forward<T>(t))呼び出しによって選択されたオーバーロードされた関数のパラメータ型を、構築してその後含まれる値の型をTjとする
    • is_same_v<decay_t<T>, variant>falseであること
    • decay_t<T>in_place_type_tおよびin_place_index_tの特殊化ではないこと
    • is_constructible_v<Tj, T>trueであること
    • FUN(std::forward<T>(x))が適格であること
  • (5) :
    • Types...内にTが一度だけ現れること
    • is_constructible_v<T, Args...>trueであること
  • (6) :
  • (7) :
    • Types...I番目の型をTiとする
    • I < sizeof...(Types)であること
    • is_constructible_v<Ti, Args...>trueであること
  • (8) :

効果

  • (1) :
    • T0型を値初期化して保持する
  • (2) :
    • otherが値を保持している場合、other.index()jとして、get<j>(other)で得られたotherが保持する値を直接初期化によって*thisに保持する
    • そうでない場合、*thisは値を保持しない
  • (3) :
    • otherが値を保持している場合、other.index()jとして、get<j>(std::move(other))で得られたotherが保持する値を直接初期化によって*thisに保持する
    • そうでない場合、*thisは値を保持しない
  • (4) :
    • std::forward<T>(t)によってTj型を直接構築して*thisに保持する
  • (5) :
    • std::forward<Args>(args)...をコンストラクタ引数としてT型オブジェクトを直接構築して*thisに保持する
  • (6) :
    • ilstd::forward<Args>(args)...をコンストラクタ引数としてT型オブジェクトを直接構築して*thisに保持する
  • (7) :
    • std::forward<Args>(args)...をコンストラクタ引数としてTi型オブジェクトを直接構築して*thisに保持する
  • (8) :
    • ilstd::forward<Args>(args)...をコンストラクタ引数としてTi型オブジェクトを直接構築して*thisに保持する
  • (9)-(16) :
    • uses-allocator構築すること以外は、対応するコンストラクタと等価

事後条件

例外

  • (1) :
    • T0型を値初期化した際、その型のコンストラクタによって任意の例外が送出される可能性がある
    • noexcept内の式は、is_nothrow_default_constructible_v<T0>と等価
  • (2) :
    • 全ての型Tiの直接初期化が、任意の例外を送出する可能性がある
  • (3) :
    • 全ての型Tiのムーブ構築が、任意の例外を送出する可能性がある
    • noexcept内の式は、全てのTiについてのis_nothrow_move_constructible_v<Ti>を論理積したものと等価
  • (4) :
    • Tjの選択された初期化方法 (コンストラクタ) が任意の例外を送出する可能性がある
    • noexcept内の式は、is_nothrow_constructible_v<Tj, T>と等価となる
  • (5), (6) :
    • Tの選択されたコンストラクタが任意の例外を送出する可能性がある
  • (7), (8) :
    • Tiの選択されたコンストラクタが任意の例外を送出する可能性がある

自明定義される条件

定数式に評価される条件

  • (1) : T0型の値初期化がconstexpr関数の要件を満たすこと
  • (4) : 型Tjの選択された初期化方法 (コンストラクタ) がconstexpr評価できること
  • (5), (6) : 型Tの選択されたコンストラクタがconstexpr評価できること
  • (7), (8) : 型Tiの選択されたコンストラクタがconstexpr評価できること

備考

  • (4) : 以下のコードは不適格となる。第1テンプレート引数の型をとるコンストラクタオーバーロードと、第2テンプレート引数の型をとるコンストラクタオーバーロードが定義されるため、曖昧になる:
    std::variant<std::string, std::string> v("abc"); // コンパイルエラー!
    

基本的な使い方

#include <cassert>
#include <variant>
#include <string>

int main()
{
  // (1)
  // デフォルト構築
  {
    // 0番目の型 (ここではint) が値初期化される
    std::variant<int, char, double> v;

    assert(v.index() == 0);
    assert(std::holds_alternative<int>(v));
    assert(std::get<int>(v) == 0); // 値初期化されるのでゼロ初期化される (不定値にはならない)
  }

  // (2)
  // コピー構築
  {
    std::variant<int, char, double> a = 1;
    std::variant<int, char, double> b = a;

    assert(a == b);
    assert(std::holds_alternative<int>(a));
    assert(std::holds_alternative<int>(b));
  }

  // (3)
  // ムーブ構築
  {
    std::variant<int, char, double> a = 1;
    std::variant<int, char, double> b = std::move(a);

    assert(std::holds_alternative<int>(b));
    assert(std::get<int>(b) == 1);
  }

  // (4)
  // 候補型のうち、いずれかの型の値を代入
  {
    std::variant<int, char, double> v = 3.14;

    assert(std::holds_alternative<double>(v));
    assert(std::get<double>(v) == 3.14);
  }

  // (5)
  // 候補型のうち、いずれかの型のコンストラクタ引数をとって、
  // コンストラクタ内でその型のオブジェクトを構築して保持する
  {
    // コンストラクタ引数3と'a'を渡して、
    // コンストラクタ内でstd::string型オブジェクトを構築する
    std::variant<int, char, std::string> v{
      std::in_place_type<std::string>,
      3,
      'a'
    };

    assert(std::holds_alternative<std::string>(v));
    assert(std::get<std::string>(v) == "aaa");
  }

  // (6)
  // (5) とほぼ同じ。コンストラクタ引数の先頭が初期化子リストの場合に、
  // こちらが呼ばれる。
  {
    std::allocator<char> alloc;
    std::variant<int, char, std::string> v{
      std::in_place_type<std::string>,
      {'H', 'e', 'l', 'l', 'o'},
      alloc
    };

    assert(std::holds_alternative<std::string>(v));
    assert(std::get<std::string>(v) == "Hello");
  }

  // (7)
  // 候補型のうち、I番目の型のコンストラクタ引数をとって、
  // コンストラクタ内でその型のオブジェクトを構築して保持する
  {
    // コンストラクタ引数3と'a'を渡して、
    // コンストラクタ内で2番目の型 (std::string) のオブジェクトを構築する
    std::variant<int, char, std::string> v{
      std::in_place_index<2>,
      3,
      'a'
    };

    assert(v.index() == 2);
    assert(std::holds_alternative<std::string>(v));
    assert(std::get<std::string>(v) == "aaa");
  }

  // (7)
  // (6) とほぼ同じ。コンストラクタ引数の先頭が初期化子リストの場合に、
  // こちらが呼ばれる。
  {
    // コンストラクタ引数3と'a'を渡して、
    // コンストラクタ内で2番目の型 (std::string) のオブジェクトを構築する
    std::allocator<char> alloc;
    std::variant<int, char, std::string> v{
      std::in_place_index<2>,
      {'H', 'e', 'l', 'l', 'o'},
      alloc
    };

    assert(v.index() == 2);
    assert(std::holds_alternative<std::string>(v));
    assert(std::get<std::string>(v) == "Hello");
  }
}

出力

あいまいになりそうな代入の例 (C++20)

#include <cassert>
#include <variant>
#include <string>

int main()
{
  // 縮小変換 (narrowing conversion) は行われないので、
  // 0がfloat型に代入されたりはしない
  {
    std::variant<float, int> v = 0;
    assert(std::holds_alternative<int>(v));
  }

  {
    // 文字列リテラルは、C++17ではstd::stringよりもboolに優先的に変換されてしまう
    std::variant<std::string, bool> v = "abc";
    assert(std::holds_alternative<std::string>(v)); // C++17ではbool、C++20ではstd::string

    std::variant<std::string> v2 = "abc";
    assert(std::holds_alternative<std::string>(v2));

    std::variant<std::string, bool> v3 = std::string("abc"); // C++17/C++20でstd::string
    assert(std::holds_alternative<std::string>(v3));
  }
}

出力

同じ型を複数指定できる状況の例

#include <cassert>
#include <variant>
#include <string>

int main()
{
  // インデックスを指定した代入なら、同じ型が複数現れてもよい。
  // 代入演算子、emplace<T>()、std::get<T>()、std::holds_alternative<T>()は使用できない。
  // emplace<I>()、std::get<I>(), index()は使用できる
  std::variant<std::string, std::string> v1 { // OK
    std::in_place_index<0>,
    "abc"
  };
  std::string& s = std::get<0>(v1);
  assert(s == "abc");

  //std::variant<std::string, std::string> v2 { // コンパイルエラー!
  //  std::in_place_type<std::string>,
  //  "abc"
  //};

  //std::variant<std::string, std::string> v3 = "abc"; // コンパイルエラー!
}

出力

バージョン

言語

  • C++17

処理系

参照