最終更新日時: 2025年07月14日 23時04分00秒 (JST)
Koichi Murase が更新

履歴 編集

function
<expected>

# std::expected::コンストラクタ(C++23)

    constexpr expected();                                   // (1)
    constexpr expected(const expected& rhs);                // (2)
    constexpr expected(expected&& rhs) noexcept(see below); // (3)

    template<class U, class G>
    constexpr explicit(see below) expected(const expected<U, G>& rhs); // (4)
    template<class U, class G>
    constexpr explicit(see below) expected(expected<U, G>&& rhs);      // (5)

    template<class U = T>
    constexpr explicit(see below) expected(U&& v);      // (6)

    template<class G>
    constexpr explicit(see below) expected(const unexpected<G>& e); // (7)
    template<class G>
    constexpr explicit(see below) expected(unexpected<G>&& e);      // (8)

    template<class... Args>
    constexpr explicit expected(in_place_t, Args&&... args); // (9)
    template<class U, class... Args>
    constexpr explicit expected(in_place_t, initializer_list<U> il, Args&&... args); // (10)

    template<class... Args>
    constexpr explicit expected(unexpect_t, Args&&... args); // (11)
    template<class U, class... Args>
    constexpr explicit expected(unexpect_t, initializer_list<U> il, Args&&... args); // (12)

## 概要

- (1) : 正常値型`T`を値初期化して保持する。
- (2) : コピーコンストラクタ。
- (3) : ムーブコンストラクタ。
- (4) : 変換可能な`expected`オブジェクトからコピー構築する。
- (5) : 変換可能な`expected`オブジェクトからムーブ構築する。
- (6) : 正常値型`T`に変換可能な型`U`の値を正常値として受け取り、コピーまたはムーブして保持する。
- (7) : 変換可能な`unexpected`オブジェクトかエラー値をコピー構築する。
- (8) : 変換可能な`unexpected`オブジェクトからエラー値をムーブ構築する。
- (9) : 正常値型`T`のコンストラクタ引数として任意個の引数を受け取って、コンストラクタ内で型`T`のオブジェクトを正常値として生成し、保持する。
- (10) : 正常値型`T`のコンストラクタ引数として初期化子リストと任意個の引数を受け取って、コンストラクタ内で型`T`のオブジェクトを正常値として生成し、保持する。
- (11) : エラー値型`E`のコンストラクタ引数として任意個の引数を受け取って、コンストラクタ内で型`E`のオブジェクトをエラー値として生成し、保持する。
- (12) : エラー値型`E`のコンストラクタ引数として初期化子リストと任意個の引数を受け取って、コンストラクタ内で型`E`のオブジェクトをエラー値として生成し、保持する。

説明用のテンプレート変数`converts-from-any-cvref`を次の通り定義する。

    template<class T, class W>
    constexpr bool converts-from-any-cvref =
      disjunction_v<is_constructible<T, W&>, is_convertible<W&, T>,
                    is_constructible<T, W>, is_convertible<W, T>,
                    is_constructible<T, const W&>, is_convertible<const W&, T>,
                    is_constructible<T, const W>, is_convertible<const W, T>>;

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

- (1) : `is_default_constructible_v<T> == true`
- (3) : `is_move_constructible_v<T> == true && is_move_constructible_v<E> == true`
- (4) : 次の制約を全て満たすこと
  - `is_constructible_v<T, const U&> == true`
  - `is_constructible_v<E, const G&> == true`
  - `converts-from-any-cvref<T, expected<U, G>> == false`
  - `is_constructible_v<unexpected<E>, expected<U, G>&> == false`
  - `is_constructible_v<unexpected<E>, expected<U, G>> == false`
  - `is_constructible_v<unexpected<E>, const expected<U, G>&> == false`
  - `is_constructible_v<unexpected<E>, const expected<U, G>> == false`
- (5) : 次の制約を全て満たすこと
  - `is_constructible_v<T, U> == true`
  - `is_constructible_v<E, G> == true`
  - `converts-from-any-cvref<T, expected<U, G>> == false`
  - `is_constructible_v<unexpected<E>, expected<U, G>&> == false`
  - `is_constructible_v<unexpected<E>, expected<U, G>> == false`
  - `is_constructible_v<unexpected<E>, const expected<U, G>&> == false`
  - `is_constructible_v<unexpected<E>, const expected<U, G>> == false`
- (6) : 次の制約を全て満たすこと
  - `is_same_v<remove_cvref_t<U>, in_place_t> == false`
  - `is_same_v<expected, remove_cvref_t<U>> == false`
  - `remove_cvref_t<U>`は`unexpected`の特殊化でない
  - `is_constructible_v<T, U> == true`
- (7) : `is_constructible_v<E, const G&> == true`
- (8) : `is_constructible_v<E, G> == true`
- (9) : `is_constructible_v<T, Args...> == true`
- (10) : `is_constructible_v<T, initializer_list<U>&, Args...> == true`
- (11) : `is_constructible_v<E, Args...> == true`
- (12) : `is_constructible_v<E, initializer_list<U>&, Args...> == true`

## 効果

- (1) : 正常値を値初期化して保持する。
- (2) : `rhs`が正常値を保持していれば、`*rhs`で正常値を直接非リスト初期化する。そうでなければ、`rhs.error()`でエラー値を直接非リスト初期化する。
- (3) : `rhs`が正常値を保持していれば、`std::move(*rhs)`で正常値を直接非リスト初期化する。そうでなければ、`std::move(rhs.error())`でエラー値を直接非リスト初期化する。
- (4) : `rhs`が正常値を保持していれば、`std::forward<const U&>(*rhs)`で正常値を直接非リスト初期化する。そうでなければ、`std::forward<const G&>(rhs.error())`でエラー値を直接非リスト初期化する。
- (5) : `rhs`が正常値を保持していれば、`std::forward<U>(*rhs)`で正常値を直接非リスト初期化する。そうでなければ、`std::forward<G>(rhs.error())`でエラー値を直接非リスト初期化する。
- (6) : `std::forward<U>(v)`で正常値を直接非リスト初期化する。
- (7) : `std::forward<const G&>(e.error())`でエラー値を直接非リスト初期化する。
- (8) : `std::forward<G>(e.error())`でエラー値を直接非リスト初期化する。
- (9) : `std::forward<Args>(args)...`で正常値を直接非リスト初期化する。
- (10) : `il, std::forward<Args>(args)...`で正常値を直接非リスト初期化する。
- (11) : `std::forward<Args>(args)...`でエラー値を直接非リスト初期化する。
- (12) : `il, std::forward<Args>(args)...`でエラー値を直接非リスト初期化する。

## 事後条件

- (1) : 正常値を保持している。
- (2) : `rhs`が正常値を保持する場合は`*this`も正常値を保持し、`rhs`がエラー値を保持する場合は`*this`もエラー値を保持する。
- (3) : `rhs`が正常値を保持する場合は`*this`も正常値を保持し、`rhs`がエラー値を保持する場合は`*this`もエラー値を保持する。`rhs.has_value()`は変化しない。
- (4), (5) : `rhs`が正常値を保持する場合は`*this`も正常値を保持し、`rhs`がエラー値を保持する場合は`*this`もエラー値を保持する。`rhs.has_value()`は変化しない。
- (6) : 正常値を保持している。
- (7), (8) : エラー値を保持している。
- (9), (10) : 正常値を保持している。
- (11), (12) : エラー値を保持している。

## 例外

- (1) : 正常値型の初期化から送出される例外。
- (2) : 正常値型またはエラー値型の初期化から送出される例外。
- (3) : 正常値型またはエラー値型の初期化から送出される例外。
  - `is_nothrow_move_constructible_v<T>`かつ`is_nothrow_move_constructible_v<E>`であれば、ムーブコンストラクタはnoexcept指定される。
- (4), (5) : 正常値型またはエラー値型の初期化から送出される例外。
- (6) : 正常値型の初期化から送出される例外。
- (7), (8) : エラー値型の初期化から送出される例外。
- (9), (10) : 正常値型の初期化から送出される例外。
- (11), (12) : エラー値型の初期化から送出される例外。

## delete定義される条件

- (2) : `!is_copy_constructible_v<T> || !is_copy_constructible_v<E>`

## トリビアルに定義される条件

- (2) : `is_trivially_copy_constructible_v<T> && is_trivially_copy_constructible_v<E>`
- (3) : `is_trivially_move_constructible_v<T> && is_trivially_move_constructible_v<E>`

## explicitになる条件

- (4) : `!is_convertible_v<const U&, T> || !is_convertible_v<const G&, E>`
- (5) : `!is_convertible_v<U, T> || !is_convertible_v<G, E>`
- (6) : `!is_convertible_v<U, T>`
- (7) : `!is_convertible_v<const G&, E>`
- (8) : `!is_convertible_v<G, E>`

## 例

    #include <cassert>
    #include <expected>
    #include <memory>
    #include <string>
    #include <string_view>
    #include <tuple>
    #include <utility>
    #include <vector>

    // std::pair型から2要素std::tuple型へはコピー変換可能
    using IntPair  = std::pair<int, int>;
    using IntTuple = std::tuple<int, int>;

    // std::unique_ptr型からstd::shared_ptr型へはムーブ変換可能
    using UniquePtr = std::unique_ptr<int>;
    using SharedPtr = std::shared_ptr<int>;

    // 引数リスト または 初期化子リスト＋引数リスト から構築可能な型
    struct ComplexType {
      std::string data;
      std::vector<int> seq;

      ComplexType(const char* ptr, size_t len)
        : data(ptr, len) {}
      ComplexType(std::initializer_list<int> il, std::string_view sv)
        : data(sv), seq(il) {}
    };

    int main()
    {
      // (1) デフォルトコンストラクタ
      {
        std::expected<int, std::string> x;
        assert(x.has_value());
        assert(x.value() == 0);
        // int型の値初期化{}は値0
      }

      // (2) コピーコンストラクタ
      {
        std::expected<int, std::string> srcV = 42;
        std::expected<int, std::string> dstV = srcV;
        assert(srcV.has_value() && dstV.has_value());
        assert(srcV.value() == 42 && dstV.value() == 42);

        std::expected<int, std::string> srcE = std::unexpected{"Oops"};
        std::expected<int, std::string> dstE = srcE;
        assert(!srcE.has_value() && !dstE.has_value());
        assert(srcE.error() == "Oops" && dstE.error() == "Oops");
      }

      // (3) ムーブコンストラクタ
      {
        std::expected<std::string, int> srcV = "ok";
        std::expected<std::string, int> dstV = std::move(srcV);
        assert(srcV.has_value() && dstV.has_value());
        assert(dstV.value() == "ok");
        // srcV.value()はstd::stringムーブ後の未規定の値

        std::expected<int, std::string> srcE = std::unexpected{"ng"};
        std::expected<int, std::string> dstE = std::move(srcE);
        assert(!srcE.has_value() && !dstE.has_value());
        assert(dstE.error() == "ng");
        // srcE.error()はstd::stringムーブ後の未規定の値
      }

      // (4) 変換コピー構築
      {
        std::expected<IntPair,  int> src = IntPair{1, 2};
        std::expected<IntTuple, int> dst = src;
        assert(src.has_value() && dst.has_value());
        assert((dst.value() == IntTuple{1, 2}));
      }

      // (5) 変換ムーブ構築
      {
        std::expected<UniquePtr, int> src = std::make_unique<int>(42);
        std::expected<SharedPtr, int> dst = std::move(src);
        assert(src.has_value() && dst.has_value());
        assert(*dst.value() == 42);
        assert(src.value() == nullptr);
        // ムーブ後のstd::unique_ptr型はnullptrが保証される
      }

      // (6) 正常値の変換コピー／ムーブ構築
      {
        IntPair src1{1, 2};
        std::expected<IntTuple, int> dst1 = src1;
        assert(dst1.has_value());
        assert((dst1.value() == IntTuple{1, 2}));

        UniquePtr src2 = std::make_unique<int>(42);
        std::expected<SharedPtr, int> dst2 = std::move(src2);
        assert(dst2.has_value());
        assert(*dst2.value() == 42);
      }

      // (7), (8) エラー値の変換コピー／ムーブ構築
      {
        std::unexpected<IntPair> src1{std::in_place, 1, 2};
        std::expected<int, IntTuple> dst1 = src1;
        assert(not dst1.has_value());
        assert((dst1.error() == IntTuple{1, 2}));

        UniquePtr src2 = std::make_unique<int>(42);
        std::expected<int, SharedPtr> dst2 = std::unexpected{std::move(src2)};
        assert(not dst2.has_value());
        assert(*dst2.error() == 42);
      }

      // (9), (10) 引数リストから正常値を直接構築
      {
        std::expected<ComplexType, int> x1{std::in_place, "C++", 1};
        assert(x1.has_value());
        assert(x1.value().data == "C");
        // "C++"より長さ1の文字列が構築されている

        std::expected<ComplexType, int> x2{std::in_place, {5, 6, 7, 8}, "Steps"};
        assert(x2.has_value());
        assert(x2.value().data == "Steps");
        assert((x2.value().seq == std::vector<int>{5, 6, 7, 8}));
      }

      // (11), (12) 引数リストからエラー値を直接構築
      {
        std::expected<int, ComplexType> x1{std::unexpect, "Hello!", 4};
        assert(not x1.has_value());
        assert(x1.error().data == "Hell");
        // "Hello!"より長さ4の文字列が構築されている

        std::expected<int, ComplexType> x2{std::unexpect, {1, 2, 3}, "Lotus"};
        assert(not x2.has_value());
        assert(x2.error().data == "Lotus");
        assert((x2.error().seq == std::vector<int>{1, 2, 3}));
      }
    }

### 出力

## バージョン

### 言語

- C++23

### 処理系

- Clang: 16.0 ✅
- GCC: 12.1 ✅
- ICC: ??
- Visual C++: ??

## 参照

- P0323R12 std::expected