最終更新日時: 2026年06月08日 10時13分52秒 (JST)
yoh が更新

履歴 編集

customization point object
<execution>

# std::execution::connect(C++26)

    namespace std::execution {
      struct connect_t;
      inline constexpr connect_t connect{};
    }

## 概要

`connect`は、SenderとReceiverを接続した結果Operation Stateを返すカスタマイゼーションポイントオブジェクトである。

呼び出し式`connect(sndr, rcvr)`は、下記の動作となる。

- `transform_sender`により`sndr`から新しいSender`new_sndr`へ変換する。（多くのケースで無変換）
- 呼び出し式が適格であるならば、`new_sndr.connect(rcvr)`を返す。
- そうでなければ、`new_sndr`をコルーチンのAwaitable型とみなして`rcvr`と接続した結果を返す。

## 効果

説明用の型`Sndr`を`decltype((sndr))`、型`Rcvr`を`decltype((rcvr))`とし、式`new_sndr`を次の通りとする。

    transform_sender(sndr, get_env(rcvr))

式`connect(sndr, rcvr)`は`rcvr`が1回だけ評価されることを除いて、下記と等価。式の型は`operation_state`を満たす。

- 適格であるならば、式`new_sndr.connect(rcvr)`
- そうでなければ、式`connect-awaitable(new_sndr, rcvr)`

このとき下記が全て`true`であること。

- `sender_in<Sndr, env_of_t<Rcvr>>`
- `receiver-of<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>>`

下記を満たす右辺値式`rcvr2`が存在するとき、プログラムは不適格(診断不要)となる。

- `decltype(rcvr2)`は`receiver`のモデルである
- `noexcept(rcvr2)`が`true`
- `is_same_v<decltype(get_env(rcvr2)), decltype(get_env(rcvr))>`が`true`
- `noexcept(execution::connect(sndr, rcvr))`が`true`
- `noexcept(execution::connect(sndr, rcvr2))`が適格かつ`false`に評価される

上記により`get_completion_signatures`内部のように、環境のコンテキストのみから`connect`操作の例外送出有無を判断できるようになる。

### Awaitable接続用へルパ

説明用のクラス`connect-awaitable-promise`, `operation-state-task`をそれぞれ下記の通り定義する。

型`DS`を`decay_t<decltype((new_sndr))>`、型`DR`を`decay_t<Rcvr>`とする。

    namespace std::execution {
      struct connect-awaitable-promise : with-await-transform<connect-awaitable-promise> {
        connect-awaitable-promise(DS&, DR& rcvr) noexcept : rcvr(rcvr) {}

        suspend_always initial_suspend() noexcept { return {}; }
        [[noreturn]] suspend_always final_suspend() noexcept { terminate(); }
        [[noreturn]] void unhandled_exception() noexcept { terminate(); }
        [[noreturn]] void return_void() noexcept { terminate(); }

        coroutine_handle<> unhandled_stopped() noexcept {
          set_stopped(std::move(rcvr));
          return noop_coroutine();
        }

        operation-state-task get_return_object() noexcept {
          return operation-state-task{
            coroutine_handle<connect-awaitable-promise>::from_promise(*this)};
        }

        env_of_t<DR> get_env() const noexcept {
          return execution::get_env(rcvr);
        }

      private:
        DR& rcvr;  // exposition only
      };
    }

    namespace std::execution {
      struct operation-state-task {  // exposition only
        using operation_state_concept = operation_state_tag;
        using promise_type = connect-awaitable-promise;

        explicit operation-state-task(coroutine_handle<> h) noexcept : coro(h) {}
        operation-state-task(operation-state-task&&) = delete;
        ~operation-state-task() { coro.destroy(); }

        void start() & noexcept {
          coro.resume();
        }

      private:
        coroutine_handle<> coro;  // exposition only
      };
    }

`C`型の`c`とコルーチンPromise型の左辺値`p`に対して、`await-result-type<C, Promise>`を`decltype(GET-AWAITER(c, p).await_resume())`型とし、`await-result-type<C>`を`decltype(GET-AWAITER(c).await_resume())`型とする。 型`V`を`await-result-type<DS, connect-awaitable-promise>`とする。

完了シグネチャ集合型 `Sigs`を下記の通り定義する。

    completion_signatures<
      SET-VALUE-SIG(V),
      set_error_t(exception_ptr),
      set_stopped_t()>

説明用の関数テンプレート`suspend-complete`とコルーチン`connect-awaitable`を下記の通り定義する。

    namespace std::execution {
      template<class Fun, class... Ts>
      auto suspend-complete(Fun fun, Ts&&... as) noexcept {  // exposition only
        auto fn = [&, fun]() noexcept { fun(std::forward<Ts>(as)...); };

        struct awaiter {
          decltype(fn) fn;  // exposition only

          static constexpr bool await_ready() noexcept { return false; }
          void await_suspend(coroutine_handle<>) noexcept { fn(); }
          [[noreturn]] void await_resume() noexcept { unreachable(); }
        };
        return awaiter{fn};
      }

      operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver-of<DR, Sigs> {
        exception_ptr ep;
        try {
          if constexpr (same_as<V, void>) {
            co_await std::move(sndr);
            co_await suspend-complete(set_value, std::move(rcvr));
          } else {
            co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr));
          }
        } catch(...) {
          ep = current_exception();
        }
        co_await suspend-complete(set_error, std::move(rcvr), std::move(ep));
      }
    }

## カスタマイゼーションポイント

Sender`sndr` 変換後の`new_sndr`に対して、式`new_sndr.connect(rcvr)`が呼び出される。

## 備考

`connect`はSender内部実装から呼び出される想定であり、実行制御ライブラリ利用者が直接利用する必要はない。

## 例

    #include <print>
    #include <execution>
    namespace ex = std::execution;

    struct ValueReceiver {
      using receiver_concept = ex::receiver_tag;

      void set_value(int v) && noexcept
      {
        std::println("{}", v);
      }
    };

    int main()
    {
      // 値42を送信するSender
      ex::sender auto sndr = ex::just(42);
      // int値を受信して表示するReceiver
      ValueReceiver rcvr;

      // SenderとReceiverを接続
      ex::operation_state auto op = ex::connect(sndr, rcvr);
      // Operation Stateを開始
      ex::start(op);
    }

### 出力

    42

## バージョン

### 言語

- C++26

### 処理系

- Clang: ??
- GCC: ??
- ICC: ??
- Visual C++: ??

## 関連項目

- `execution::sender`
- `execution::receiver`
- `execution::operation_state`

## 参照

- P2300R10 `std::execution`
- P3396R1 std::execution wording fixes
- P3557R3 High-Quality Sender Diagnostics with Constexpr Exceptions
- P3388R3 When Do You Know connect Doesn't Throw?
- LWG 4208. Wording needs to ensure that in `connect(sndr, rcvr)` that `rcvr` expression is only evaluated once
- P3826R5 Fix Sender Algorithm Customization
- P4159R0 Make `sender_in` and `receiver_of` exposition-only