@@ -17,7 +17,7 @@ namespace std::execution {
1717
1818`let_value`は[パイプ可能Senderアダプタオブジェクト](sender_adaptor_closure.md)であり、パイプライン記法をサポートする。
1919
20- 本ページにてSenderアルゴリズム`let_value`/[`let_error`](let_error.md)/[`let_stopped`](let_stopped.md)の動作仕様を包括的に説明するため、以降のセクションにおいては`let-cpo`, `set-cpo`をそれぞれ下記の通りとする。
20+ 本ページにてSenderアルゴリズム`let_value`/[`let_error`](let_error.md)/[`let_stopped`](let_stopped.md)の動作仕様を包括的に説明するため、以降のセクションにおいては`let-cpo`, `set-cpo`をそれぞれ下記の通りとする。また`let-tag`を`let_value`/`let_error`/`let_stopped`それぞれに対して一意な空のクラスとする。
2121
2222| `let-cpo` | `set-cpo` |
2323|----|----|
@@ -38,16 +38,33 @@ transform_sender(get-domain-early(sndr), make-sender(let-cpo, f, sndr))
3838* get-domain-early[ link get-domain-early.md]
3939* make-sender[ link make-sender.md]
4040
41+ 説明用のクラステンプレート` let-data ` を下記の通り定義する。
4142
42- ### Senderアルゴリズムタグ ` let-cpo `
43+ ``` cpp
44+ template <class Sndr , class Fn>
45+ struct let-data {
46+ Sndr sndr; // exposition only
47+ Fn fn; // exposition only
48+ };
49+ ```
50+
51+ 式`let-cpo.transform_sender(s, es...)`は、`s`が1回だけ評価されることを除いて、下記と等価。
52+
53+ ```cpp
54+ make-sender(let-tag{}, let-data{s.template get<2>(), s.template get<1>()})
55+ ```
56+ * make-sender[ link make-sender.md]
57+ * let-tag[ italic]
58+
59+ ### Senderアルゴリズムタグ ` let-tag `
4360Senderアルゴリズム動作説明用のクラステンプレート[ ` impls-for ` ] ( impls-for.md ) に対して、下記の特殊化が定義される。
4461
4562``` cpp
4663namespace std ::execution {
4764 template<>
48- struct impls-for<decayed-typeof< let-cpo > > : default-impls {
65+ struct impls-for<let-tag > : default-impls {
4966 static constexpr auto get-state = see below;
50- static constexpr auto complete = see below;
67+ static constexpr auto start = see below;
5168
5269 template<class Sndr, class... Env>
5370 static consteval void check-types();
@@ -56,38 +73,25 @@ namespace std::execution {
5673```
5774* impls-for[link impls-for.md]
5875* default-impls[link impls-for.md]
59- * decayed-typeof[link /reference/functional/decayed-typeof.md]
60- * let-cpo[italic]
76+ * let-tag[italic]
6177
62- `impls-for<decayed-typeof<let-cpo>>::get-state`メンバは、下記ラムダ式と等価な関数呼び出し可能なオブジェクトで初期化される。
63-
64- - `args_variant_t` : 入力Sender`sndr`の完了シグネチャ集合から求まる送信値リスト型情報(`variant<monostate, tuple<...>, ...>`)
65- - `ops2_variant_t` : `f`が返すSenderに対応する非同期操作型情報(`variant<monostate, {OperationState型}, ...>`)
66- - 戻り値`state-type`型オブジェクトを下記の通り初期化する。同オブジェクトは`complete`メンバから呼ばれる`let-bind`で利用される。
67- - `fn` : Senderアルゴリズム構築時に指定した関数呼び出し可能オブジェクト`f`
68- - `env` : 入力Sender`sndr`に関連付けられた[属性](../queryable.md)
69- - `args` : `f`呼び出し時の引数リスト格納用変数(空値`monostate`で初期化)
70- - `ops` : `f`が返すSenderに対応する非同期操作の格納用変数(空値`monostate`で初期化)
78+ `impls-for<let-tag>::get-state`メンバは、下記ラムダ式と等価な関数呼び出し可能なオブジェクトで初期化される。
7179
7280```cpp
7381[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires see below {
74- auto& [_, fn, child] = sndr;
82+ auto& [_, data] = sndr;
83+ auto& [child, fn] = data;
84+ using child_t = decltype(std::forward_like<Sndr>(child));
7585 using fn_t = decay_t<decltype(fn)>;
76- using env_t = decltype(let-env(child));
7786 using args_variant_t = see below;
78- using ops2_variant_t = see below;
79-
80- struct state-type {
81- fn_t fn; // exposition only
82- env_t env; // exposition only
83- args_variant_t args; // exposition only
84- ops2_variant_t ops2; // exposition only
85- };
86- return state-type{allocator-aware-forward(std::forward_like<Sndr>(fn), rcvr),
87- let-env(child), {}, {}};
87+ using ops_variant_t = see below;
88+ using state_t = let-state<decayed-typeof<set-cpo>, child_t, fn_t, Rcvr,
89+ args_variant_t, ops_variant_t>;
90+ return state_t(std::forward_like<Sndr>(child), std::forward_like<Sndr>(fn), rcvr);
8891}
8992```
90- * allocator-aware-forward[ link allocator-aware-forward.md]
93+ * decayed-typeof[ link /reference/functional/decayed-typeof.md]
94+ * set-cpo[ italic]
9195
9296- 説明用のパック` Sigs ` を[ ` completion_signatures_of_t ` ] ( completion_signatures_of_t.md ) ` < ` [ ` child-type ` ] ( child-type.md ) ` <Sndr>, ` [ ` FWD-ENV-T ` ] ( ../forwarding_query.md ) ` ( ` [ ` env_of_t ` ] ( env_of_t.md ) ` <Rcvr>)> ` による[ ` completion_signatures ` ] ( completion_signatures.md ) 特殊化のテンプレートパラメータとし、パック` LetSigs ` を` Sigs ` に含まれる型のうち戻り値型が[ ` decayed-typeof ` ] ( /reference/functional/decayed-typeof.md ) ` <set-cpo> ` に等しいものと定義する。説明用のエイリアステンプレート` as-tuple<Tag(Args...)> ` を[ ` decayed-tuple ` ] ( decayed-tuple.md ) ` <Args...> ` と定義する。型` args_variant_t ` は下記定義において重複削除した型となる。
9397
@@ -97,38 +101,28 @@ namespace std::execution {
97101 * variant[link /reference/variant/variant.md]
98102 * monostate[link /reference/variant/monostate.md]
99103
100- - 説明用の型`Tag`とパック`Args`に対して、説明用のエイリアステンプレート`as-sndr2<Tag(Args...)>`を[`call-result-t`](/reference/functional/call-result-t.md)`<Fn,` [`decay_t `](/reference/type_traits/decay.md)`<Args>&...>`と定義する。型`ops2_variant_t `は下記定義において重複削除した型となる。
104+ - 説明用の型`Tag`とパック`Args`に対して、説明用のエイリアステンプレート`as-sndr2<Tag(Args...)>`を[`call-result-t`](/reference/functional/call-result-t.md)`<Fn,` [`decay_t `](/reference/type_traits/decay.md)`<Args>&...>`と定義する。型`ops_variant_t `は下記定義において重複削除した型となる。
101105
102106 ```cpp
103- variant<monostate, connect_result_t <as-sndr2<LetSigs>, receiver2<Rcvr, env_t >>...>
107+ variant<monostate,
108+ connect_result_t <child_t , let-state::receiver,
109+ connect_result_t <as-sndr2<LetSigs>, receiver2<Rcvr, env_t >>...>
104110 ```
105111 * variant[link /reference/variant/variant.md]
106112 * monostate[link /reference/variant/monostate.md]
107113 * connect_result_t [link connect_result_t .md]
108114
109115- 型`args_variant_t `および`ops2_variant_t `が適格なときに限って、上記ラムダ式のrequires 節が満たされる。
110116
111- `impls-for <decayed-typeof <let-cpo>>::complete`メンバは、下記ラムダ式と等価な関数呼び出し可能なオブジェクトで初期化される。
112-
113- - 完了関数`set-cpo`の場合、Sender構築時の引数`f`に対して`f(args...)`を呼び出し、戻り値[Sender](sender.md)から入れ子非同期操作を開始する。同Senderの完了結果を接続先[Receiver](receiver.md)へ転送する。
114- - それ以外の完了操作の場合、接続先[Receiver](receiver.md)の同種完了関数へ転送する。
117+ `impls-for <let-tag>::start`メンバは、下記ラムダ式と等価な関数呼び出し可能なオブジェクトで初期化される。
115118
116119```cpp
117- []<class Tag , class... Args>
118- (auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {
119- if constexpr (same_as<Tag, decayed-typeof<set-cpo >>) {
120- TRY-EVAL(rcvr, let-bind(state, rcvr, std::forward<Args >(args)...));
121- } else {
122- Tag()(std::move(rcvr), std::forward<Args >(args)...);
123- }
124- }
120+ []<class State , class Rcvr>(State& state, Rcvr&) noexcept {
121+ start(get<typename State::op_t >(state.ops));
122+ }
125123```
126- * decayed-typeof[link /reference/functional/decayed-typeof.md]
127- * TRY-EVAL[link set_value.md]
128- * std::move[link /reference/utility/move.md]
129- * set-cpo[italic]
130124
131- メンバ関数`impls-for<decayed-typeof< let-cpo> >::check-types`の効果は下記の通り。
125+ メンバ関数`impls-for<let-tag >::check-types`の効果は下記の通り。
132126
133127```cpp
134128using LetFn = remove_cvref_t<data-type<Sndr>>;
@@ -149,13 +143,6 @@ cs.for-each(overload-set(fn, [](auto){}));
149143* overload-set[ link overload-set.md]
150144* set-cpo[ italic]
151145
152- 説明用の変数` is-valid-let-sender ` は下記を全て満たす時に限って` true ` となる。
153-
154- - ` ( ` [ ` constructible_from ` ] ( /reference/concepts/constructible_from.md ) ` <decay_t<Ts>, Ts> &&...) `
155- - [ ` invocable ` ] ( /reference/concepts/invocable.md ) ` <LetFn, decay_t<Ts>&...> `
156- - [ ` sender ` ] ( sender.md ) ` < ` [ ` invoke_result_t ` ] ( /reference/type_traits/invoke_result.md ) ` <LetFn, decay_t<Ts>&...>> `
157- - パック` env-t ` を` decltype(let-cpo.transform_env(declval<Sndr>(), declval<Env>())) ` としたとき、` sizeof...(Env) == 0 || ` [ ` sender_in ` ] ( sender_in.md ) ` < ` [ ` invoke_result_t ` ] ( /reference/type_traits/invoke_result.md ) ` <LetFn, decay_t<Ts>&...>, env-t...> `
158-
159146説明用の式` sndr ` と` env ` に対して、型` Sndr ` を` decltype((sndr)) ` とする。[ ` sender-for ` ] ( sender-for.md ) ` <Sndr, ` [ ` decayed-typeof ` ] ( /reference/functional/decayed-typeof.md ) ` <let-cpo>> == false ` のとき、式` let-cpo.transform_env(sndr, env) ` は不適格となる。
160147
161148そうでなければ、式` let-cpo.transform_env(sndr, env) ` は下記と等価。
@@ -169,47 +156,115 @@ return JOIN-ENV(let-env(child), FWD-ENV(env));
169156
170157
171158## 説明専用エンティティ
159+ ### 式` let-env `
172160説明用の式` sndr ` を用いて、` let-env(sndr) ` を下記リストのうち最初に適格となる式と定義する。
173161
174162- [ ` SCHED-ENV ` ] ( schedule.md ) ` ( ` [ ` get_completion_scheduler ` ] ( get_completion_scheduler.md ) ` < ` [ ` decayed-typeof ` ] ( /reference/functional/decayed-typeof.md ) ` <set-cpo>>( ` [ ` get_env ` ] ( get_env.md ) ` (sndr))) `
175163- [ ` MAKE-ENV ` ] ( ../queryable.md ) ` ( ` [ ` get_domain ` ] ( get_domain.md ) ` , ` [ ` get_domain ` ] ( get_domain.md ) ` ( ` [ ` get_env ` ] ( get_env.md ) ` (sndr))) `
176164- ` (void(sndr), ` [ ` env<>{} ` ] ( env.md ) ` ) `
177165
178- 説明専用の` let-bind ` テンプレート関数を下記の通り定義する。
166+ ### 変数` is-valid-let-sender `
167+ 説明用の変数` is-valid-let-sender ` は下記を全て満たす時に限って` true ` となる。
179168
180- - 入力Senderの完了結果から引数リスト` state.args ` を構築し、Senderアルゴリズム構築時に指定した関数呼び出し可能オブジェクト` state.fn ` を呼び出す。
181- - 上記呼び出しで` state.fn ` から返された[ Sender] ( sender.md ) と、完了結果をSenderアルゴリズムの接続先[ Receiver] ( receiver.md ) ` Rcvr ` へ転送するヘルパ` receiver2 ` を[ 接続(connect)] ( connect.md ) する。
182- - 接続結果[ Operation State] ( operation_state.md ) を` state.op2 ` 上に構築し、入れ子の非同期操作を[ 開始(start)] ( start.md ) する。
169+ - ` ( ` [ ` constructible_from ` ] ( /reference/concepts/constructible_from.md ) ` <decay_t<Ts>, Ts> &&...) `
170+ - [ ` invocable ` ] ( /reference/concepts/invocable.md ) ` <LetFn, decay_t<Ts>&...> `
171+ - [ ` sender ` ] ( sender.md ) ` < ` [ ` invoke_result_t ` ] ( /reference/type_traits/invoke_result.md ) ` <LetFn, decay_t<Ts>&...>> `
172+ - パック` env-t ` を` decltype(let-cpo.transform_env(declval<Sndr>(), declval<Env>())) ` としたとき、` sizeof...(Env) == 0 || ` [ ` sender_in ` ] ( sender_in.md ) ` < ` [ ` invoke_result_t ` ] ( /reference/type_traits/invoke_result.md ) ` <LetFn, decay_t<Ts>&...>, env-t...> `
183173
174+ ### クラステンプレート` let-state `
184175``` cpp
185- namespace std ::execution {
186- template<class State, class Rcvr, class... Args>
187- void let-bind(State& state, Rcvr& rcvr, Args&&... args); // exposition only
188- }
189- ```
176+ template <class Cpo , class Sndr, class Fn, class Rcvr, class ArgsVariant,
177+ class OpsVariant>
178+ struct let-state {
179+ using env_t = decltype(let-env(declval<Sndr >())); // exposition only
180+ Fn fn; // exposition only
181+ env_t env; // exposition only
182+ ArgsVariant args; // exposition only
183+ OpsVariant ops; // exposition only
184+
185+ template<class Tag, class... Ts>
186+ constexpr void impl(Rcvr& rcvr, Tag tag, Ts&&... ts) noexcept
187+ { // exposition only
188+ using args_t = decayed-tuple<Ts...>;
189+ using receiver_type = receiver2<Rcvr, env_t>;
190+ using sender_type = apply_result_t<Fn, args_t&>;
191+ if constexpr (is_same_v<Tag, Cpo>) {
192+ try {
193+ auto& tuple = args.template emplace<args_t>(std::forward<Ts>(ts)...);
194+ ops.template emplace<monostate>();
195+ auto&& sndr = apply(std::move(fn), tuple);
196+ using op_t = connect_result_t<sender_type, receiver_type>;
197+ auto mkop2 = [&] {
198+ return connect(std::forward<sender_type>(sndr),
199+ receiver_type{rcvr, env});
200+ };
201+ auto& op = ops.template emplace<op_t>(emplace-from{mkop2});
202+ start(op);
203+ } catch (...) {
204+ constexpr bool nothrow =
205+ is_nothrow_constructible_v<args_t, Ts...> &&
206+ is_nothrow_applicable_v<Fn, args_t&> &&
207+ noexcept(
208+ connect(
209+ declval<sender_type>(),
210+ receiver_type{rcvr, env}));
211+ if constexpr (!nothrow) {
212+ set_error(std::move(rcvr), current_exception());
213+ }
214+ }
215+ } else {
216+ tag (std::move (rcvr), std::forward<Ts >(ts)...);
217+ }
218+ }
190219
191- `let-bind`テンプレート関数の効果は下記と等価。
220+ struct receiver { // exposition only
221+ let-state& state; // exposition only
222+ Rcvr& rcvr; // exposition only
192223
193- ```cpp
194- using args_t = decayed-tuple<Args...>;
195- auto mkop2 = [&] {
196- return connect(
197- apply(std::move(state.fn),
198- state.args.template emplace<args_t>(std::forward<Args>(args)...)),
199- receiver2{rcvr, std::move(state.env)});
224+ using receiver_concept = receiver_t;
225+
226+ template<class... Args>
227+ constexpr void set_value(Args&&... args) noexcept {
228+ state.impl(rcvr, execution::set_value, std::forward<Args>(args)...);
229+ }
230+ template<class... Args>
231+ constexpr void set_error(Args&&... args) noexcept {
232+ state.impl(rcvr, execution::set_error, std::forward<Args>(args)...);
233+ }
234+ template<class... Args>
235+ constexpr void set_stopped(Args&&... args) noexcept {
236+ state.impl(rcvr, execution::set_stopped, std::forward<Args>(args)...);
237+ }
238+
239+ constexpr env_of_t<const Rcvr&> get_env() const noexcept {
240+ return execution::get_env(rcvr);
241+ }
242+ };
243+
244+ using op_t = connect_result_t<Sndr, receiver>; // exposition only
245+ constexpr let-state(Sndr&& sndr, Fn fn, Rcvr& rcvr) // exposition only
246+ : fn(std::move(fn)), env(let-env(sndr)),
247+ ops(in_place_type<op_t>, std::forward<Sndr >(sndr),
248+ receiver{* this, rcvr})
249+ {}
200250};
201- start(state.ops2.template emplace<decltype(mkop2())>(emplace-from{mkop2}));
202251```
203- * decayed-tuple [ link decayed-tuple .md]
252+ * connect_result_t [ link connect_result_t .md]
204253* connect[ link connect.md]
205254* start[ link start.md]
206- * emplace-from[ link emplace-from.md]
207- * apply[ link /reference/tuple/apply.md]
208- * template emplace[ link /reference/variant/variant/emplace.md]
209- * std::move[ link /reference/utility/move.md]
210-
211- 説明専用のテンプレートクラス` receiver2 ` を下記の通り定義する。
212-
255+ * receiver_t[ link receiver.md]
256+ * execution::set_value[ link set_value.md]
257+ * execution::set_error[ link set_error.md]
258+ * execution::set_stopped[ link set_stopped.md]
259+ * execution::get_env[ link get_env.md]
260+ * apply_result_t[ link /reference/type_traits/apply_result.md]
261+ * is_same_v[ link /reference/type_traits/is_same.md]
262+ * current_exception()[ link /reference/exception/current_exception.md]
263+ * is_nothrow_constructible_v[ link /reference/type_traits/is_nothrow_constructible.md]
264+ * is_nothrow_applicable_v[ link /reference/type_traits/is_nothrow_applicable.md]
265+ * in_place_type[ link /reference/utility/in_place_type_t.md]
266+
267+ ### クラステンプレート` receiver2 `
213268``` cpp
214269namespace std ::execution {
215270 template<class Rcvr, class Env>
@@ -445,3 +500,4 @@ catch 0
445500- [LWG 4203. Constraints on `get-state` functions are incorrect](https://cplusplus.github.io/LWG/issue4203)
446501- [LWG 4204. specification of `as-sndr2(Sig)` in [exec.let] is incomplete](https://cplusplus.github.io/LWG/issue4204)
447502- [LWG 4205. `let_[*].transform_env` is specified in terms of the `let_*` sender itself instead of its child](https://cplusplus.github.io/LWG/issue4205)
503+ - [P3373R4 Of Operation States and Their Lifetimes](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p3373r4.pdf)
0 commit comments