[exec.snd] # 33 Execution control library [[exec]](./#exec) ## 33.9 Senders [exec.snd] ### [33.9.1](#general) General [[exec.snd.general]](exec.snd.general) [1](#general-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1325) Subclauses [[exec.factories]](#exec.factories "33.9.11 Sender factories") and [[exec.adapt]](#exec.adapt "33.9.12 Sender adaptors") define customizable algorithms that return senders[.](#general-1.sentence-1) Each algorithm has a default implementation[.](#general-1.sentence-2) Let sndr be the result of an invocation of such an algorithm or an object equal to the result ([[concepts.equality]](concepts.equality "18.2 Equality preservation")), and let Sndr be decltype((sndr))[.](#general-1.sentence-3) Let rcvr be a receiver of type Rcvr with associated environment env of type Env such that [sender_to](#concept:sender_to "33.9.3 Sender concepts [exec.snd.concepts]") is true[.](#general-1.sentence-4) For the default implementation of the algorithm that produced sndr, connecting sndr to rcvr and starting the resulting operation state ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) necessarily results in the potential evaluation ([[basic.def.odr]](basic.def.odr "6.3 One-definition rule")) of a set of completion operations whose first argument is a subexpression equal to rcvr[.](#general-1.sentence-5) Let Sigs be a pack of completion signatures corresponding to this set of completion operations, and let CS be the type of the expression get_completion_signatures()[.](#general-1.sentence-6) Then CS is a specialization of the class template completion_signatures ([[exec.cmplsig]](exec.cmplsig "33.10 Completion signatures")), the set of whose template arguments is Sigs[.](#general-1.sentence-7) If none of the types in Sigs are dependent on the type Env, then the expression get_completion_signatures() is well-formed and its type is CS[.](#general-1.sentence-8) If a user-provided implementation of the algorithm that produced sndr is selected instead of the default: - [(1.1)](#general-1.1) Any completion signature that is in the set of types denoted by completion_signatures_of_t and that is not part of Sigs shall correspond to error or stopped completion operations, unless otherwise specified[.](#general-1.1.sentence-1) - [(1.2)](#general-1.2) If none of the types in Sigs are dependent on the type Env, thencompletion_signatures_of_t andcompletion_signatures_of_t shall denote the same type[.](#general-1.2.sentence-1) ### [33.9.2](#expos) Exposition-only entities [[exec.snd.expos]](exec.snd.expos) [1](#expos-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1373) Subclause [exec.snd] makes use of the following exposition-only entities[.](#expos-1.sentence-1) [2](#expos-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1376) For a queryable object env,*FWD-ENV*(env) is an expression whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that for a query object q and a pack of subexpressions as, the expression *FWD-ENV*(env).query(q, as...) is ill-formed if forwarding_query(q) is false; otherwise, it is expression-equivalent to env.query(q, as...)[.](#expos-2.sentence-1) The type *FWD-ENV-T*(Env) isdecltype(*FWD-ENV*(declval()))[.](#expos-2.sentence-2) [3](#expos-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1390) For a query object q and a subexpression v,*MAKE-ENV*(q, v) is an expression env whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that the result of env.query(q) has a value equal to v ([[concepts.equality]](concepts.equality "18.2 Equality preservation"))[.](#expos-3.sentence-1) Unless otherwise stated, the object to which env.query(q) refers remains valid while env remains valid[.](#expos-3.sentence-2) [4](#expos-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1400) For two queryable objects env1 and env2, a query object q, and a pack of subexpressions as,*JOIN-ENV*(env1, env2) is an expression env3 whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that env3.query(q, as...) is expression-equivalent to: - [(4.1)](#expos-4.1) env1.query(q, as...) if that expression is well-formed, - [(4.2)](#expos-4.2) otherwise, env2.query(q, as...) if that expression is well-formed, - [(4.3)](#expos-4.3) otherwise, env3.query(q, as...) is ill-formed[.](#expos-4.sentence-1) [5](#expos-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1416) The results of *FWD-ENV*, *MAKE-ENV*, and *JOIN-ENV* can be context-dependent; i.e., they can evaluate to expressions with different types and value categories in different contexts for the same arguments[.](#expos-5.sentence-1) [6](#expos-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1423) For a scheduler sch,*SCHED-ATTRS*(sch) is an expression o1 whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that o1.query(get_completion_scheduler) is an expression with the same type and value as sch where Tag is one of set_value_t or set_stopped_t, and such that o1.query(get_domain) is expression-equivalent tosch.query(get_domain)[.](#expos-6.sentence-1) *SCHED-ENV*(sch) is an expression o2 whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that o2.query(get_scheduler) is a prvalue with the same type and value as sch, and such that o2.query(get_domain) is expression-equivalent tosch.query(get_domain)[.](#expos-6.sentence-2) [7](#expos-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1439) For two subexpressions rcvr and expr,*SET-VALUE*(rcvr, expr) is expression-equivalent to(expr, set_value(std​::​move(rcvr))) if the type of expr is void; otherwise, set_value(std​::​move(rcvr), expr)[.](#expos-7.sentence-1) *TRY-EVAL*(rcvr, expr) is equivalent to:try { expr;} catch(...) { set_error(std::move(rcvr), current_exception());} if expr is potentially-throwing; otherwise, expr[.](#expos-7.sentence-2) *TRY-SET-VALUE*(rcvr, expr) is*TRY-EVAL*(rcvr, *SET-VALUE*(rcvr, expr)) except that rcvr is evaluated only once[.](#expos-7.sentence-3) [🔗](#expos-itemdecl:1) `template constexpr auto completion-domain(const Sndr& sndr) noexcept; ` [8](#expos-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1466) *COMPL-DOMAIN*(T) is the type of the expressionget_domain(get_completion_scheduler(get_env(sndr)))[.](#expos-8.sentence-1) [9](#expos-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1470) *Effects*: If all of the types*COMPL-DOMAIN*(set_value_t),*COMPL-DOMAIN*(set_error_t), and *COMPL-DOMAIN*(set_stopped_t) are ill-formed,completion-domain(sndr) is a default-constructed prvalue of type Default[.](#expos-9.sentence-1) Otherwise, if they all share a common type ([[meta.trans.other]](meta.trans.other "21.3.9.7 Other transformations")) (ignoring those types that are ill-formed), then *completion-domain*(sndr) is a default-constructed prvalue of that type[.](#expos-9.sentence-2) Otherwise, *completion-domain*(sndr) is ill-formed[.](#expos-9.sentence-3) [🔗](#expos-itemdecl:2) `template constexpr decltype(auto) query-with-default( Tag, const Env& env, Default&& value) noexcept(see below); ` [10](#expos-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1492) Let e be the expression Tag()(env) if that expression is well-formed; otherwise, it is static_cast(std​::​forward(value))[.](#expos-10.sentence-1) [11](#expos-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1497) *Returns*: e[.](#expos-11.sentence-1) [12](#expos-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1501) *Remarks*: The expression in the noexcept clause is noexcept(e)[.](#expos-12.sentence-1) [🔗](#expos-itemdecl:3) `template constexpr auto get-domain-early(const Sndr& sndr) noexcept; ` [13](#expos-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1512) *Effects*: Equivalent to:return Domain(); where Domain is the decayed type of the first of the following expressions that is well-formed: - [(13.1)](#expos-13.1) get_domain(get_env(sndr)) - [(13.2)](#expos-13.2) *completion-domain*(sndr) - [(13.3)](#expos-13.3) default_domain() [🔗](#expos-itemdecl:4) `template constexpr auto get-domain-late(const Sndr& sndr, const Env& env) noexcept; ` [14](#expos-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1533) *Effects*: Equivalent to: - [(14.1)](#expos-14.1) If [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") is true, thenreturn Domain(); where Domain is the type of the following expression:[] {auto [_, sch, _] = sndr; return *query-with-default*(get_domain, sch, default_domain());}(); [*Note [1](#expos-note-1)*: The continues_on algorithm works in tandem with schedule_from ([[exec.schedule.from]](#exec.schedule.from "33.9.12.7 execution​::​schedule_­from")) to give scheduler authors a way to customize both how to transition onto (continues_on) and off of (schedule_from) a given execution context[.](#expos-14.1.sentence-1) Thus, continues_on ignores the domain of the predecessor and uses the domain of the destination scheduler to select a customization, a property that is unique to continues_on[.](#expos-14.1.sentence-2) That is why it is given special treatment here[.](#expos-14.1.sentence-3) — *end note*] - [(14.2)](#expos-14.2) Otherwise,return Domain(); where Domain is the first of the following expressions that is well-formed and whose type is not void: * [(14.2.1)](#expos-14.2.1) get_domain(get_env(sndr)) * [(14.2.2)](#expos-14.2.2) *completion-domain*(sndr) * [(14.2.3)](#expos-14.2.3) get_domain(env) * [(14.2.4)](#expos-14.2.4) get_domain(get_scheduler(env)) * [(14.2.5)](#expos-14.2.5) default_domain() [15](#expos-15) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1577) template<[*callable*](functional.syn#concept:callable "22.10.2 Header synopsis [functional.syn]") Fun>requires is_nothrow_move_constructible_vstruct *emplace-from* { Fun *fun*; // *exposition only*using type = *call-result-t*; constexpr operator type() && noexcept([*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header synopsis [functional.syn]")) {return std::move(fun)(); }constexpr type operator()() && noexcept([*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header synopsis [functional.syn]")) {return std::move(fun)(); }}; [*Note [2](#expos-note-2)*: *emplace-from* is used to emplace non-movable types into tuple, optional, variant, and similar types[.](#expos-15.sentence-1) — *end note*] [16](#expos-16) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1599) struct *on-stop-request* { inplace_stop_source& *stop-src*; // *exposition only*void operator()() noexcept { *stop-src*.request_stop(); }}; [17](#expos-17) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1607) templatestruct *product-type* { // *exposition only* T0 t0; // *exposition only* T1 t1; // *exposition only* ⋮ Tn tn; // *exposition only*templateconstexpr decltype(auto) *get*(this Self&& self) noexcept; // *exposition only*templateconstexpr decltype(auto) *apply*(this Self&& self, Fn&& fn) // *exposition only*noexcept(*see below*);}; [18](#expos-18) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1625) [*Note [3](#expos-note-3)*: *product-type* is presented here in pseudo-code form for the sake of exposition[.](#expos-18.sentence-1) It can be approximated in standard C++ with a tuple-like implementation that takes care to keep the type an aggregate that can be used as the initializer of a structured binding declaration[.](#expos-18.sentence-2) — *end note*] [*Note [4](#expos-note-4)*: An expression of type *product-type* is usable as the initializer of a structured binding declaration ([[dcl.struct.bind]](dcl.struct.bind "9.7 Structured binding declarations"))[.](#expos-18.sentence-3) — *end note*] [🔗](#expos-itemdecl:5) `template constexpr decltype(auto) get(this Self&& self) noexcept; ` [19](#expos-19) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1644) *Effects*: Equivalent to:auto& [...ts] = self;return std::forward_like(ts...[I]); templateconstexpr decltype(auto) *apply*(this Self&& self, Fn&& fn) noexcept(*see below*); [20](#expos-20) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1659) *Constraints*: The expression in the return statement below is well-formed[.](#expos-20.sentence-1) [21](#expos-21) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1663) *Effects*: Equivalent to:auto& [...ts] = self;return std::forward(fn)(std::forward_like(ts)...); [22](#expos-22) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1671) *Remarks*: The expression in the noexcept clause is true if the return statement above is not potentially throwing; otherwise, false[.](#expos-22.sentence-1) [23](#expos-23) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1678) Let [*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]") be the following concept:namespace std::execution {template class T, class... Args>concept [*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]") = // *exposition only*requires { typename T; };} [🔗](#expos-itemdecl:6) `template constexpr auto make-sender(Tag tag, Data&& data, Child&&... child); ` [24](#expos-24) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1694) *Mandates*: The following expressions are true: - [(24.1)](#expos-24.1) [semiregular](concepts.object#concept:semiregular "18.6 Object concepts [concepts.object]") - [(24.2)](#expos-24.2) [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]") - [(24.3)](#expos-24.3) ([sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") && ...) - [(24.4)](#expos-24.4) [dependent_sender](#concept:dependent_sender "33.9.3 Sender concepts [exec.snd.concepts]") || [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]"), where Sndr is *basic-sender* as defined below[.](#expos-24.sentence-1) *Recommended practice*: If evaluation of [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") results in an uncaught exception from the evaluation of get_completion_signatures(), the implementation should include information about that exception in the resulting diagnostic[.](#expos-24.4.sentence-2) [25](#expos-25) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1714) *Returns*: A prvalue of type *basic-sender*, decay_t...> that has been direct-list-initialized with the forwarded arguments, where *basic-sender* is the following exposition-only class template except as noted below[.](#expos-25.sentence-1) [26](#expos-26) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1722) *Remarks*: The default template argument for the Data template parameter denotes an unspecified empty trivially copyable class type that models [semiregular](concepts.object#concept:semiregular "18.6 Object concepts [concepts.object]")[.](#expos-26.sentence-1) namespace std::execution {templateconcept [*completion-tag*](#concept:completion-tag "33.9.2 Exposition-only entities [exec.snd.expos]") = // *exposition only*[same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]") || [same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]") || [same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]"); struct *default-impls* { // *exposition only*static constexpr auto *get-attrs* = *see below*; // *exposition only*static constexpr auto *get-env* = *see below*; // *exposition only*static constexpr auto *get-state* = *see below*; // *exposition only*static constexpr auto *start* = *see below*; // *exposition only*static constexpr auto *complete* = *see below*; // *exposition only*templatestatic consteval void *check-types*(); // *exposition only*}; templatestruct *impls-for* : *default-impls* {}; // *exposition only*template // *exposition only*using *state-type* = decay_t<*call-result-t*>::*get-state*), Sndr, Rcvr&>>; template // *exposition only*using *env-type* = *call-result-t*>::*get-env*), Index, *state-type*&, const Rcvr&>; templateusing *data-type* = decltype(declval().template *get*<1>()); // *exposition only*templateusing *child-type* = decltype(declval().template *get*()); // *exposition only*templateusing *indices-for* = remove_reference_t::*indices-for*; // *exposition only*templatestruct *basic-state* { // *exposition only**basic-state*(Sndr&& sndr, Rcvr&& rcvr) noexcept(*see below*): *rcvr*(std::move(rcvr)) , *state*(*impls-for*>::*get-state*(std::forward(sndr), *rcvr*)) { } Rcvr *rcvr*; // *exposition only**state-type* *state*; // *exposition only*}; templaterequires [*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]")<*env-type*, Index, Sndr, Rcvr>struct *basic-receiver* { // *exposition only*using receiver_concept = receiver_t; using *tag-t* = tag_of_t; // *exposition only*using *state-t* = *state-type*; // *exposition only*static constexpr const auto& *complete* = *impls-for*<*tag-t*>::*complete*; // *exposition only*templaterequires [*callable*](functional.syn#concept:callable "22.10.2 Header synopsis [functional.syn]")void set_value(Args&&... args) && noexcept {*complete*(Index(), op->*state*, op->*rcvr*, set_value_t(), std::forward(args)...); }templaterequires [*callable*](functional.syn#concept:callable "22.10.2 Header synopsis [functional.syn]")void set_error(Error&& err) && noexcept {*complete*(Index(), op->*state*, op->*rcvr*, set_error_t(), std::forward(err)); }void set_stopped() && noexceptrequires [*callable*](functional.syn#concept:callable "22.10.2 Header synopsis [functional.syn]") {*complete*(Index(), op->*state*, op->*rcvr*, set_stopped_t()); }auto get_env() const noexcept -> *env-type* {return *impls-for*::*get-env*(Index(), op->*state*, op->*rcvr*); }*basic-state** *op*; // *exposition only*}; constexpr auto *connect-all* = *see below*; // *exposition only*templateusing *connect-all-result* = *call-result-t*< // *exposition only*decltype(*connect-all*), *basic-state**, Sndr, *indices-for*>; templaterequires [*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]")<*state-type*, Sndr, Rcvr> &&[*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]")<*connect-all-result*, Sndr, Rcvr>struct *basic-operation* : *basic-state* { // *exposition only*using operation_state_concept = operation_state_t; using *tag-t* = tag_of_t; // *exposition only**connect-all-result* *inner-ops*; // *exposition only**basic-operation*(Sndr&& sndr, Rcvr&& rcvr) noexcept(*see below*) // *exposition only*: *basic-state*(std::forward(sndr), std::move(rcvr)), *inner-ops*(*connect-all*(this, std::forward(sndr), *indices-for*())){}void start() & noexcept {auto& [...ops] = *inner-ops*; *impls-for*::*start*(this->*state*, this->*rcvr*, ops...); }}; templatestruct *basic-sender* : *product-type* { // *exposition only*using sender_concept = sender_t; using *indices-for* = index_sequence_for; // *exposition only*decltype(auto) get_env() const noexcept {auto& [_, data, ...child] = *this; return *impls-for*::*get-attrs*(data, child...); }template<[*decays-to*](execution.syn#concept:decays-to "33.4 Header synopsis [execution.syn]")<*basic-sender*> Self, [receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") Rcvr>auto connect(this Self&& self, Rcvr rcvr) noexcept(*see below*)-> *basic-operation* {return {std::forward(self), std::move(rcvr)}; }template<[*decays-to*](execution.syn#concept:decays-to "33.4 Header synopsis [execution.syn]")<*basic-sender*> Self, class... Env>static constexpr auto get_completion_signatures(); };} [27](#expos-27) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1857) It is unspecified whether a specialization of *basic-sender* is an aggregate[.](#expos-27.sentence-1) [28](#expos-28) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1861) An expression of type *basic-sender* is usable as the initializer of a structured binding declaration ([[dcl.struct.bind]](dcl.struct.bind "9.7 Structured binding declarations"))[.](#expos-28.sentence-1) [29](#expos-29) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1865) The expression in the noexcept clause of the constructor of *basic-state* isis_nothrow_move_constructible_v &&[*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header synopsis [functional.syn]")>::*get-state*), Sndr, Rcvr&> &&([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")<*state-type*, *get-state-result*> || is_nothrow_constructible_v<*state-type*, *get-state-result*>) where *get-state-result* is*call-result-t*>::*get-state*), Sndr, Rcvr&>. [30](#expos-30) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1879) The object *connect-all* is initialized with a callable object equivalent to the following lambda: [🔗](#expos-itemdecl:7) `[]( basic-state* op, Sndr&& sndr, index_sequence) noexcept(see below) -> decltype(auto) { auto& [_, data, ...child] = sndr; return product-type{connect( std::forward_like(child), basic-receiver>{op})...}; } ` [31](#expos-31) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1894) *Constraints*: The expression in the return statement is well-formed[.](#expos-31.sentence-1) [32](#expos-32) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1898) *Remarks*: The expression in the noexcept clause is true if the return statement is not potentially throwing; otherwise, false[.](#expos-32.sentence-1) [33](#expos-33) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1905) The expression in the noexcept clause of the constructor of *basic-operation* is:is_nothrow_constructible_v<*basic-state*, Self, Rcvr> &&noexcept(*connect-all*(this, std::forward(sndr), *indices-for*())) [34](#expos-34) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1913) The expression in the noexcept clause of the connect member function of *basic-sender* is:is_nothrow_constructible_v<*basic-operation*, Self, Rcvr> [35](#expos-35) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1920) The member *default-impls*​::​*get-attrs* is initialized with a callable object equivalent to the following lambda:[](const auto&, const auto&... child) noexcept -> decltype(auto) {if constexpr (sizeof...(child) == 1)return (*FWD-ENV*(get_env(child)), ...); elsereturn env<>();} [36](#expos-36) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1932) The member *default-impls*​::​*get-env* is initialized with a callable object equivalent to the following lambda:[](auto, auto&, const auto& rcvr) noexcept -> decltype(auto) {return *FWD-ENV*(get_env(rcvr));} [37](#expos-37) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1941) The member *default-impls*​::​*get-state* is initialized with a callable object equivalent to the following lambda:[](Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) {auto& [_, data, ...child] = sndr; return *allocator-aware-forward*(std::forward_like(data), rcvr);} [38](#expos-38) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1951) The member *default-impls*​::​*start* is initialized with a callable object equivalent to the following lambda:[](auto&, auto&, auto&... ops) noexcept -> void {(execution::start(ops), ...);} [39](#expos-39) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1960) The member *default-impls*​::​*complete* is initialized with a callable object equivalent to the following lambda:[]( Index, auto& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires [*callable*](functional.syn#concept:callable "22.10.2 Header synopsis [functional.syn]") {static_assert(Index::value == 0); Tag()(std::move(rcvr), std::forward(args)...);} [🔗](#lib:check-types,default-impls) `template static consteval void default-impls::check-types(); ` [40](#expos-40) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1979) Let Is be the pack of integral template arguments of the integer_sequence specialization denoted by*indices-for*[.](#expos-40.sentence-1) [41](#expos-41) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1984) *Effects*: Equivalent to:(get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>(), ...); [42](#expos-42) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1991) [*Note [5](#expos-note-5)*: For any types T and S, and pack E, let e be the expression*impls-for*​::​*check-types*()[.](#expos-42.sentence-1) Then exactly one of the following is true: - [(42.1)](#expos-42.1) e is ill-formed, or - [(42.2)](#expos-42.2) the evaluation of e exits with an exception, or - [(42.3)](#expos-42.3) e is a core constant expression[.](#expos-42.sentence-2) When e is a core constant expression, the pack S, E... uniquely determines a set of completion signatures[.](#expos-42.sentence-3) — *end note*] [🔗](#lib:get_completion_signatures,basic-sender) `template template constexpr auto basic-sender::get_completion_signatures(); ` [43](#expos-43) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2015) Let Rcvr be the type of a receiver whose environment has type E, whereE is the first type in the list Env..., env<>[.](#expos-43.sentence-1) Let *CHECK-TYPES*() be the expression*impls-for*​::​template *check-types*< Sndr, E>(), and let CS be a type determined as follows: - [(43.1)](#expos-43.1) If *CHECK-TYPES*() is a core constant expression, let op be an lvalue subexpression whose type is connect_result_t[.](#expos-43.1.sentence-1) Then CS is the specialization of completion_signatures the set of whose template arguments correspond to the set of completion operations that are potentially evaluated ([[basic.def.odr]](basic.def.odr "6.3 One-definition rule")) as a result of evaluating op.start()[.](#expos-43.1.sentence-2) - [(43.2)](#expos-43.2) Otherwise, CS is completion_signatures<>[.](#expos-43.2.sentence-1) [44](#expos-44) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2038) *Constraints*: *CHECK-TYPES*() is a well-formed expression[.](#expos-44.sentence-1) [45](#expos-45) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2042) *Effects*: Equivalent to:*CHECK-TYPES*();return CS(); [46](#expos-46) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2051) templatestruct *overload-set* : Fns... {using Fns::operator()...;}; [47](#expos-47) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2061) struct *not-a-sender* {using sender_concept = sender_t; templatestatic consteval auto get_completion_signatures() -> completion_signatures<> {throw *unspecified-exception*(); }}; where *unspecified-exception* is a type derived from exception[.](#expos-47.sentence-1) [48](#expos-48) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2078) constexpr void *decay-copyable-result-datums*(auto cs) { cs.*for-each*([](Tag(*)(Ts...)) {if constexpr (!(is_constructible_v, Ts> &&...))throw *unspecified-exception*(); });} where *unspecified-exception* is a type derived from exception[.](#expos-48.sentence-1) [🔗](#expos-itemdecl:10) `template decltype(auto) allocator-aware-forward(T&& obj, Context&& context); // exposition only ` [49](#expos-49) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2098) *allocator-aware-forward* is an exposition-only function template used to either create a new object of type remove_cvref_t from obj or forward obj depending on whether an allocator is available[.](#expos-49.sentence-1) If the environment associated with context provides an allocator (i.e., the expression get_allocator(get_env(context)) is valid), let *alloc* be the result of this expression and let P be remove_cvref_t[.](#expos-49.sentence-2) [50](#expos-50) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2108) *Returns*: - [(50.1)](#expos-50.1) If *alloc* is not defined, returns std​::​forward(obj), - [(50.2)](#expos-50.2) otherwise if P is a specialization of *product-type*, returns an object of type P whose elements are initialized usingmake_obj_using_allocator(std::forward_like(e), *alloc*) where e is the corresponding element of obj, - [(50.3)](#expos-50.3) otherwise, returns make_obj_using_allocator

(std​::​forward(obj), *alloc*)[.](#expos-50.sentence-1) ### [33.9.3](#concepts) Sender concepts [[exec.snd.concepts]](exec.snd.concepts) [1](#concepts-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2127) The [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") concept defines the requirements for a sender type ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations"))[.](#concepts-1.sentence-1) The [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") concept defines the requirements for a sender type that can create asynchronous operations given an associated environment type[.](#concepts-1.sentence-2) The [sender_to](#concept:sender_to "33.9.3 Sender concepts [exec.snd.concepts]") concept defines the requirements for a sender type that can connect with a specific receiver type[.](#concepts-1.sentence-3) The get_env customization point object is used to access a sender's associated attributes[.](#concepts-1.sentence-4) The connect customization point object is used to connect ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) a sender and a receiver to produce an operation state[.](#concepts-1.sentence-5) [🔗](#lib:is-dependent-sender-helper) namespace std::execution {templateconcept [*is-constant*](#concept:is-constant "33.9.3 Sender concepts [exec.snd.concepts]") = true; // *exposition only*templateconcept [*is-sender*](#concept:is-sender "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]"); templateconcept [*enable-sender*](#concept:enable-sender "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[*is-sender*](#concept:is-sender "33.9.3 Sender concepts [exec.snd.concepts]") ||[*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]")>>; // [[exec.awaitable]](#exec.awaitable "33.9.4 Awaitable helpers")templateinline constexpr bool enable_sender = [*enable-sender*](#concept:enable-sender "33.9.3 Sender concepts [exec.snd.concepts]"); templateconsteval bool *is-dependent-sender-helper*() try { // *exposition only* get_completion_signatures(); return false; } catch (dependent_sender_error&) {return true; }templateconcept [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") = enable_sender> &&requires (const remove_cvref_t& sndr) {{ get_env(sndr) } -> [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]"); } &&[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13 Concept move_­constructible [concept.moveconstructible]")> &&[constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_­from [concept.constructible]"), Sndr>; templateconcept [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") =[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") &&(sizeof...(Env) <= 1) &&([*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") &&...) &&[*is-constant*](#concept:is-constant "33.9.3 Sender concepts [exec.snd.concepts]")()>; templateconcept [dependent_sender](#concept:dependent_sender "33.9.3 Sender concepts [exec.snd.concepts]") =[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") && bool_constant<*is-dependent-sender-helper*()>::value; templateconcept [sender_to](#concept:sender_to "33.9.3 Sender concepts [exec.snd.concepts]") =[sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")> &&[receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1 Receiver concepts [exec.recv.concepts]")>> &&requires (Sndr&& sndr, Rcvr&& rcvr) { connect(std::forward(sndr), std::forward(rcvr)); };} [2](#concepts-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2197) For a type Sndr, if[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") is true and[dependent_sender](#concept:dependent_sender "33.9.3 Sender concepts [exec.snd.concepts]") is false, then Sndr is a non-dependent sender ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations"))[.](#concepts-2.sentence-1) [3](#concepts-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2203) Given a subexpression sndr, let Sndr be decltype((sndr)) and let rcvr be a receiver with an associated environment whose type is Env[.](#concepts-3.sentence-1) A completion operation is a [*permissible completion*](#def:completion,permissible "33.9.3 Sender concepts [exec.snd.concepts]") for Sndr and Env if its completion signature appears in the argument list of the specialization of completion_signatures denoted bycompletion_signatures_of_t[.](#concepts-3.sentence-2) Sndr and Env model [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") if all the completion operations that are potentially evaluated by connecting sndr to rcvr and starting the resulting operation state are permissible completions for Sndr and Env[.](#concepts-3.sentence-3) [4](#concepts-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2218) *Remarks*: Pursuant to [[namespace.std]](namespace.std "16.4.5.2.1 Namespace std"), users may specialize enable_sender totrue for cv-unqualified program-defined types that model [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), andfalse for types that do not[.](#concepts-4.sentence-1) Such specializations shall be usable in constant expressions ([[expr.const]](expr.const "7.7 Constant expressions")) and have type const bool[.](#concepts-4.sentence-2) [5](#concepts-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2229) The exposition-only concepts[*sender-of*](#concept:sender-of "33.9.3 Sender concepts [exec.snd.concepts]") and [*sender-in-of*](#concept:sender-in-of "33.9.3 Sender concepts [exec.snd.concepts]") define the requirements for a sender type that completes with a given unique set of value result types[.](#concepts-5.sentence-1) namespace std::execution {templateusing *value-signature* = set_value_t(As...); // *exposition only*templateconcept [*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") &&*MATCHING-SIG*(SetValue, // see [[exec.general]](exec.general "33.1 General")*gather-signatures*, *value-signature*, type_identity_t>); templateconcept [*sender-in-of*](#concept:sender-in-of "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3 Sender concepts [exec.snd.concepts]"); templateconcept [*sender-of*](#concept:sender-of "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3 Sender concepts [exec.snd.concepts]");} [6](#concepts-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2258) Let sndr be an expression such that decltype((sndr)) is Sndr[.](#concepts-6.sentence-1) The type tag_of_t is as follows: - [(6.1)](#concepts-6.1) If the declarationauto&& [tag, data, ...children] = sndr; would be well-formed, tag_of_t is an alias for decltype(auto(tag)). - [(6.2)](#concepts-6.2) Otherwise, tag_of_t is ill-formed. [7](#concepts-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2274) Let [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") be an exposition-only concept defined as follows:namespace std::execution {templateconcept [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") =[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") &&[same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]"), Tag>;} [8](#concepts-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2285) For a type T,*SET-VALUE-SIG*(T) denotes the type set_value_t() if T is cv void; otherwise, it denotes the type set_value_t(T)[.](#concepts-8.sentence-1) [9](#concepts-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2291) Library-provided sender types - [(9.1)](#concepts-9.1) always expose an overload of a member connect that accepts an rvalue sender and - [(9.2)](#concepts-9.2) only expose an overload of a member connect that accepts an lvalue sender if they model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]")[.](#concepts-9.sentence-1) ### [33.9.4](#exec.awaitable) Awaitable helpers [[exec.awaitable]](exec.awaitable) [1](#exec.awaitable-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2304) The sender concepts recognize awaitables as senders[.](#exec.awaitable-1.sentence-1) For [[exec]](exec "33 Execution control library"), an [*awaitable*](#def:awaitable "33.9.4 Awaitable helpers [exec.awaitable]") is an expression that would be well-formed as the operand of a co_await expression within a given context[.](#exec.awaitable-1.sentence-2) [2](#exec.awaitable-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2310) For a subexpression c, let *GET-AWAITER*(c, p) be expression-equivalent to the series of transformations and conversions applied to c as the operand of an [*await-expression*](expr.await#nt:await-expression "7.6.2.4 Await [expr.await]") in a coroutine, resulting in lvalue e as described by [[expr.await]](expr.await "7.6.2.4 Await"), where p is an lvalue referring to the coroutine's promise, which has type Promise[.](#exec.awaitable-2.sentence-1) [*Note [1](#exec.awaitable-note-1)*: This includes the invocation of the promise type's await_transform member if any, the invocation of the operator co_await picked by overload resolution if any, and any necessary implicit conversions and materializations[.](#exec.awaitable-2.sentence-2) — *end note*] Let *GET-AWAITER*(c) be expression-equivalent to *GET-AWAITER*(c, q) where q is an lvalue of an unspecified empty class type *none-such* that lacks an await_transform member, and where coroutine_handle<*none-such*> behaves ascoroutine_handle[.](#exec.awaitable-2.sentence-3) [3](#exec.awaitable-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2334) Let [*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]") be the following exposition-only concept:namespace std {templateconcept [*await-suspend-result*](#concept:await-suspend-result "33.9.4 Awaitable helpers [exec.awaitable]") = *see below*; // *exposition only*templateconcept [*is-awaiter*](#concept:is-awaiter "33.9.4 Awaitable helpers [exec.awaitable]") = // *exposition only*requires (A& a, coroutine_handle h) { a.await_ready() ? 1 : 0; { a.await_suspend(h) } -> [*await-suspend-result*](#concept:await-suspend-result "33.9.4 Awaitable helpers [exec.awaitable]"); a.await_resume(); }; templateconcept [*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]") = // *exposition only*requires (C (*fc)() noexcept, Promise&... p) {{ *GET-AWAITER*(fc(), p...) } -> [*is-awaiter*](#concept:is-awaiter "33.9.4 Awaitable helpers [exec.awaitable]"); };} [*await-suspend-result*](#concept:await-suspend-result "33.9.4 Awaitable helpers [exec.awaitable]") is true if and only if one of the following is true: - [(3.1)](#exec.awaitable-3.1) T is void, or - [(3.2)](#exec.awaitable-3.2) T is bool, or - [(3.3)](#exec.awaitable-3.3) T is a specialization of coroutine_handle[.](#exec.awaitable-3.sentence-2) [4](#exec.awaitable-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2365) For a subexpression c such that decltype((c)) is type C, and an lvalue p of type Promise,*await-result- type* denotes the type decltype(*GET-AWAITER*(c, p).await_resume()) and*await-result-type* denotes the type decltype(*GET-AWAITER*(c).await_resume())[.](#exec.awaitable-4.sentence-1) [5](#exec.awaitable-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2374) Let *with-await-transform* be the exposition-only class template:namespace std::execution {templateconcept [*has-as-awaitable*](#concept:has-as-awaitable "33.9.4 Awaitable helpers [exec.awaitable]") = // *exposition only*requires (T&& t, Promise& p) {{ std::forward(t).as_awaitable(p) } -> [*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]"); }; templatestruct *with-await-transform* { // *exposition only*template T&& await_transform(T&& value) noexcept {return std::forward(value); }template<[*has-as-awaitable*](#concept:has-as-awaitable "33.9.4 Awaitable helpers [exec.awaitable]") T>auto await_transform(T&& value)noexcept(noexcept(std::forward(value).as_awaitable(declval())))-> decltype(std::forward(value).as_awaitable(declval())) {return std::forward(value).as_awaitable(static_cast(*this)); }};} [6](#exec.awaitable-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2401) Let *env-promise* be the exposition-only class template:namespace std::execution {templatestruct *env-promise* : *with-await-transform*<*env-promise*> { // *exposition only**unspecified* get_return_object() noexcept; *unspecified* initial_suspend() noexcept; *unspecified* final_suspend() noexcept; void unhandled_exception() noexcept; void return_void() noexcept; coroutine_handle<> unhandled_stopped() noexcept; const Env& get_env() const noexcept; };} [*Note [2](#exec.awaitable-note-2)*: Specializations of *env-promise* are used only for the purpose of type computation; its members need not be defined[.](#exec.awaitable-6.sentence-1) — *end note*] ### [33.9.5](#exec.domain.default) execution​::​default_domain [[exec.domain.default]](exec.domain.default) [1](#exec.domain.default-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2425) namespace std::execution {struct [default_domain](#lib:default_domain "33.9.5 execution​::​default_­domain [exec.domain.default]") {template<[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]")... Env>requires (sizeof...(Env) <= 1)static constexpr [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)noexcept(*see below*); template<[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") Env>static constexpr [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept; templatestatic constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)noexcept(*see below*); };} [🔗](#lib:transform_sender,default_domain) `template<[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]")... Env> requires (sizeof...(Env) <= 1) constexpr [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") decltype(auto) transform_sender(Sndr&& sndr, const Env&... env) noexcept(see below); ` [2](#exec.domain.default-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2453) Let e be the expressiontag_of_t().transform_sender(std::forward(sndr), env...) if that expression is well-formed; otherwise, std​::​forward(sndr)[.](#exec.domain.default-2.sentence-1) [3](#exec.domain.default-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2461) *Returns*: e[.](#exec.domain.default-3.sentence-1) [4](#exec.domain.default-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2465) *Remarks*: The exception specification is equivalent to noexcept(e)[.](#exec.domain.default-4.sentence-1) [🔗](#lib:transform_env,default_domain) `template<[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") Env> constexpr [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept; ` [5](#exec.domain.default-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2477) Let e be the expressiontag_of_t().transform_env(std::forward(sndr), std::forward(env)) if that expression is well-formed; otherwise, *FWD-ENV*(std​::​forward(env))[.](#exec.domain.default-5.sentence-1) [6](#exec.domain.default-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2485) *Mandates*: noexcept(e) is true[.](#exec.domain.default-6.sentence-1) [7](#exec.domain.default-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2489) *Returns*: e[.](#exec.domain.default-7.sentence-1) [🔗](#lib:apply_sender,default_domain) `template constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args) noexcept(see below); ` [8](#exec.domain.default-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2502) Let e be the expression Tag().apply_sender(std::forward(sndr), std::forward(args)...) [9](#exec.domain.default-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2508) *Constraints*: e is a well-formed expression[.](#exec.domain.default-9.sentence-1) [10](#exec.domain.default-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2512) *Returns*: e[.](#exec.domain.default-10.sentence-1) [11](#exec.domain.default-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2516) *Remarks*: The exception specification is equivalent to noexcept(e)[.](#exec.domain.default-11.sentence-1) ### [33.9.6](#transform) execution​::​transform_sender [[exec.snd.transform]](exec.snd.transform) [🔗](#lib:transform_sender) `namespace std::execution { template requires (sizeof...(Env) <= 1) constexpr [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") decltype(auto) transform_sender(Domain dom, Sndr&& sndr, const Env&... env) noexcept(see below); } ` [1](#transform-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2534) Let *transformed-sndr* be the expressiondom.transform_sender(std::forward(sndr), env...) if that expression is well-formed; otherwise,default_domain().transform_sender(std::forward(sndr), env...) Let *final-sndr* be the expression *transformed-sndr* if *transformed-sndr* and *sndr* have the same type ignoring cv-qualifiers; otherwise, it is the expression transform_sender(dom, *transformed-sndr*, env...)[.](#transform-1.sentence-2) [2](#transform-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2549) *Returns*: *final-sndr*[.](#transform-2.sentence-1) [3](#transform-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2553) *Remarks*: The exception specification is equivalent tonoexcept(*final-sndr*)[.](#transform-3.sentence-1) ### [33.9.7](#transform.env) execution​::​transform_env [[exec.snd.transform.env]](exec.snd.transform.env) [🔗](#lib:transform_env) `namespace std::execution { template constexpr [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") decltype(auto) transform_env(Domain dom, Sndr&& sndr, Env&& env) noexcept; } ` [1](#transform.env-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2570) Let e be the expressiondom.transform_env(std::forward(sndr), std::forward(env)) if that expression is well-formed; otherwise,default_domain().transform_env(std::forward(sndr), std::forward(env)) [2](#transform.env-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2580) *Mandates*: noexcept(e) is true[.](#transform.env-2.sentence-1) [3](#transform.env-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2584) *Returns*: e[.](#transform.env-3.sentence-1) ### [33.9.8](#apply) execution​::​apply_sender [[exec.snd.apply]](exec.snd.apply) [🔗](#lib:apply_sender) `namespace std::execution { template constexpr decltype(auto) apply_sender(Domain dom, Tag, Sndr&& sndr, Args&&... args) noexcept(see below); } ` [1](#apply-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2601) Let e be the expressiondom.apply_sender(Tag(), std::forward(sndr), std::forward(args)...) if that expression is well-formed; otherwise,default_domain().apply_sender(Tag(), std::forward(sndr), std::forward(args)...) [2](#apply-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2611) *Constraints*: The expression e is well-formed[.](#apply-2.sentence-1) [3](#apply-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2615) *Returns*: e[.](#apply-3.sentence-1) [4](#apply-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2619) *Remarks*: The exception specification is equivalent to noexcept(e)[.](#apply-4.sentence-1) ### [33.9.9](#exec.getcomplsigs) execution​::​get_completion_signatures [[exec.getcomplsigs]](exec.getcomplsigs) [🔗](#exec.getcomplsigs-itemdecl:1) `template consteval auto get_completion_signatures() -> [valid-completion-signatures](execution.syn#concept:valid-completion-signatures "33.4 Header synopsis [execution.syn]") auto; ` [1](#exec.getcomplsigs-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2634) Let except be an rvalue subexpression of an unspecified class type Except such that[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13 Concept move_­constructible [concept.moveconstructible]") && [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]") is true[.](#exec.getcomplsigs-1.sentence-1) Let *CHECKED-COMPLSIGS*(e) be e if e is a core constant expression whose type satisfies [*valid-completion-signatures*](execution.syn#concept:valid-completion-signatures "33.4 Header synopsis [execution.syn]"); otherwise, it is the following expression:(e, throw except, completion_signatures()) Let *get-complsigs*() be expression-equivalent toremove_reference_t​::​template get_completion_signatures()[.](#exec.getcomplsigs-1.sentence-3) Let NewSndr be Sndr if sizeof...(Env) == 0 is true; otherwise, decltype(s) where s is the following expression:transform_sender(*get-domain-late*(declval(), declval()...), declval(), declval()...) [2](#exec.getcomplsigs-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2660) *Constraints*: sizeof...(Env) <= 1 is true[.](#exec.getcomplsigs-2.sentence-1) [3](#exec.getcomplsigs-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2664) *Effects*: Equivalent to: return e; where e is expression-equivalent to the following: - [(3.1)](#exec.getcomplsigs-3.1) *CHECKED-COMPLSIGS*(*get-complsigs*()) if *get-complsigs*() is a well-formed expression[.](#exec.getcomplsigs-3.1.sentence-1) - [(3.2)](#exec.getcomplsigs-3.2) Otherwise,*CHECKED-COMPLSIGS*(*get-complsigs*()) if *get-complsigs*() is a well-formed expression[.](#exec.getcomplsigs-3.2.sentence-1) - [(3.3)](#exec.getcomplsigs-3.3) Otherwise,completion_signatures<*SET-VALUE-SIG*(*await-result-type*...>), // [[exec.snd.concepts]](#concepts "33.9.3 Sender concepts") set_error_t(exception_ptr), set_stopped_t()> if [*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]")...> is true[.](#exec.getcomplsigs-3.3.sentence-1) - [(3.4)](#exec.getcomplsigs-3.4) Otherwise,(throw *dependent-sender-error*(), completion_signatures()) if sizeof...( Env) == 0 is true, where *dependent-sender-error* isdependent_sender_error or an unspecified type derived publicly and unambiguously fromdependent_sender_error[.](#exec.getcomplsigs-3.4.sentence-1) - [(3.5)](#exec.getcomplsigs-3.5) Otherwise,(throw except, completion_signatures())[.](#exec.getcomplsigs-3.5.sentence-1) [4](#exec.getcomplsigs-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2706) Given a type Env, ifcompletion_signatures_of_t andcompletion_signatures_of_t are both well-formed, they shall denote the same type[.](#exec.getcomplsigs-4.sentence-1) [5](#exec.getcomplsigs-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2713) Let rcvr be an rvalue whose type Rcvr models [receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]"), and let Sndr be the type of a sender such that [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")> is true[.](#exec.getcomplsigs-5.sentence-1) Let Sigs... be the template arguments of the completion_signatures specialization named by completion_signatures_of_t>[.](#exec.getcomplsigs-5.sentence-2) Let CSO be a completion function[.](#exec.getcomplsigs-5.sentence-3) If sender Sndr or its operation state cause the expression CSO(rcvr, args...) to be potentially evaluated ([[basic.def.odr]](basic.def.odr "6.3 One-definition rule")) then there shall be a signature Sig in Sigs... such that*MATCHING-SIG*(*decayed-typeof*(decltype(args)...), Sig) is true ([[exec.general]](exec.general "33.1 General"))[.](#exec.getcomplsigs-5.sentence-4) ### [33.9.10](#exec.connect) execution​::​connect [[exec.connect]](exec.connect) [1](#exec.connect-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2734) connect connects ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) a sender with a receiver[.](#exec.connect-1.sentence-1) [2](#exec.connect-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2737) The name connect denotes a customization point object[.](#exec.connect-2.sentence-1) For subexpressions sndr and rcvr, let Sndr be decltype((sndr)) andRcvr be decltype((rcvr)), let new_sndr be the expressiontransform_sender(decltype(*get-domain-late*(sndr, get_env(rcvr))){}, sndr, get_env(rcvr)) and let DS and DR bedecay_t and decay_t, respectively[.](#exec.connect-2.sentence-2) [3](#exec.connect-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2749) Let *connect-awaitable-promise* be the following exposition-only class: 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 get_env() const noexcept {return execution::get_env(*rcvr*); }private: DR& *rcvr*; // *exposition only*};} [4](#exec.connect-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2783) Let *operation-state-task* be the following exposition-only class:namespace std::execution {struct *operation-state-task* { // *exposition only*using operation_state_concept = operation_state_t; 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*};} [5](#exec.connect-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2805) Let V name the type*await-result-type*, let Sigs name the typecompletion_signatures<*SET-VALUE-SIG*(V), // see [[exec.snd.concepts]](#concepts "33.9.3 Sender concepts") set_error_t(exception_ptr), set_stopped_t()> and let *connect-awaitable* be an exposition-only coroutine defined as follows:namespace std::execution {templateauto *suspend-complete*(Fun fun, Ts&&... as) noexcept { // *exposition only*auto fn = [&, fun]() noexcept { fun(std::forward(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](exec.recv.concepts#concept:receiver_of "33.7.1 Receiver concepts [exec.recv.concepts]") { exception_ptr ep; try {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {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)); }} [6](#exec.connect-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2850) The expression connect(sndr, rcvr) is expression-equivalent to: - [(6.1)](#exec.connect-6.1) new_sndr.connect(rcvr) if that expression is well-formed[.](#exec.connect-6.1.sentence-1) *Mandates*: The type of the expression above satisfies [operation_state](exec.opstate.general#concept:operation_state "33.8.1 General [exec.opstate.general]")[.](#exec.connect-6.1.sentence-2) - [(6.2)](#exec.connect-6.2) Otherwise, *connect-awaitable*(new_sndr, rcvr)[.](#exec.connect-6.2.sentence-1) Except that rcvr is evaluated only once[.](#exec.connect-6.sentence-2) *Mandates*: The following are true: - [(6.3)](#exec.connect-6.3) [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")> - [(6.4)](#exec.connect-6.4) [receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1 Receiver concepts [exec.recv.concepts]")>> ### [33.9.11](#exec.factories) Sender factories [[exec.factories]](exec.factories) #### [33.9.11.1](#exec.schedule) execution​::​schedule [[exec.schedule]](exec.schedule) [1](#exec.schedule-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2877) schedule obtains a schedule sender ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) from a scheduler[.](#exec.schedule-1.sentence-1) [2](#exec.schedule-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2881) The name schedule denotes a customization point object[.](#exec.schedule-2.sentence-1) For a subexpression sch, the expression schedule(sch) is expression-equivalent tosch.schedule()[.](#exec.schedule-2.sentence-2) [3](#exec.schedule-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2887) *Mandates*: The type of sch.schedule() satisfies [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.schedule-3.sentence-1) [4](#exec.schedule-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2891) If the expressionget_completion_scheduler(get_env(sch.schedule())) == sch is ill-formed or evaluates to false, the behavior of calling schedule(sch) is undefined[.](#exec.schedule-4.sentence-1) #### [33.9.11.2](#exec.just) execution​::​just, execution​::​just_error, execution​::​just_stopped [[exec.just]](exec.just) [1](#exec.just-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2901) just, just_error, and just_stopped are sender factories whose asynchronous operations complete synchronously in their start operation with a value completion operation, an error completion operation, or a stopped completion operation, respectively[.](#exec.just-1.sentence-1) [2](#exec.just-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2908) The names just, just_error, and just_stopped denote customization point objects[.](#exec.just-2.sentence-1) Let *just-cpo* be one ofjust, just_error, or just_stopped[.](#exec.just-2.sentence-2) For a pack of subexpressions ts, let Ts be the pack of types decltype((ts))[.](#exec.just-2.sentence-3) The expression *just-cpo*(ts...) is ill-formed if - [(2.1)](#exec.just-2.1) ([*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]") &&...) is false, or - [(2.2)](#exec.just-2.2) *just-cpo* is just_error andsizeof...(ts) == 1 is false, or - [(2.3)](#exec.just-2.3) *just-cpo* is just_stopped andsizeof...(ts) == 0 is false[.](#exec.just-2.sentence-4) Otherwise, it is expression-equivalent to*make-sender*(*just-cpo*, *product-type*{ts...})[.](#exec.just-2.sentence-5) For just, just_error, and just_stopped, let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#exec.just-2.sentence-6) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for *just-cpo* as follows:namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<*just-cpo*>> : *default-impls* {static constexpr auto *start* =[](auto& state, auto& rcvr) noexcept -> void {auto& [...ts] = state; *set-cpo*(std::move(rcvr), std::move(ts)...); }; };} #### [33.9.11.3](#exec.read.env) execution​::​read_env [[exec.read.env]](exec.read.env) [1](#exec.read.env-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2950) read_env is a sender factory for a sender whose asynchronous operation completes synchronously in its start operation with a value completion result equal to a value read from the receiver's associated environment[.](#exec.read.env-1.sentence-1) [2](#exec.read.env-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2956) read_env is a customization point object[.](#exec.read.env-2.sentence-1) For some query object q, the expression read_env(q) is expression-equivalent to*make-sender*(read_env, q)[.](#exec.read.env-2.sentence-2) [3](#exec.read.env-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2962) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for read_env as follows: [🔗](#lib:impls-for%3cdecayed-typeof%3cread_env%3e%3e) namespace std::execution {template<>struct *impls-for*<*decayed-typeof*> : *default-impls* {static constexpr auto start =[](auto query, auto& rcvr) noexcept -> void {*TRY-SET-VALUE*(rcvr, query(get_env(rcvr))); }; }; templatestatic consteval void *check-types*();} [🔗](#lib:check-types,impls-for%3cdecayed-typeof%3cread_env%3e%3e) `template static consteval void check-types(); ` [4](#exec.read.env-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2988) Let Q be decay_t<*data-type*>[.](#exec.read.env-4.sentence-1) [5](#exec.read.env-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2991) *Throws*: An exception of an unspecified type derived from exception if the expression Q()(env) is ill-formed or has type void, whereenv is an lvalue subexpression whose type is Env[.](#exec.read.env-5.sentence-1) ### [33.9.12](#exec.adapt) Sender adaptors [[exec.adapt]](exec.adapt) #### [33.9.12.1](#exec.adapt.general) General [[exec.adapt.general]](exec.adapt.general) [1](#exec.adapt.general-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3002) Subclause [[exec.adapt]](#exec.adapt "33.9.12 Sender adaptors") specifies a set of sender adaptors[.](#exec.adapt.general-1.sentence-1) [2](#exec.adapt.general-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3005) The bitwise inclusive or operator is overloaded for the purpose of creating sender chains[.](#exec.adapt.general-2.sentence-1) The adaptors also support function call syntax with equivalent semantics[.](#exec.adapt.general-2.sentence-2) [3](#exec.adapt.general-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3010) Unless otherwise specified: - [(3.1)](#exec.adapt.general-3.1) A sender adaptor is prohibited from causing observable effects, apart from moving and copying its arguments, before the returned sender is connected with a receiver using connect, and start is called on the resulting operation state[.](#exec.adapt.general-3.1.sentence-1) - [(3.2)](#exec.adapt.general-3.2) A parent sender ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) with a single child sender sndr has an associated attribute object equal to*FWD-ENV*(get_env(sndr)) ([[exec.fwd.env]](exec.fwd.env "33.5.1 forwarding_­query"))[.](#exec.adapt.general-3.2.sentence-1) - [(3.3)](#exec.adapt.general-3.3) A parent sender with more than one child sender has an associated attributes object equal to env<>{}[.](#exec.adapt.general-3.3.sentence-1) - [(3.4)](#exec.adapt.general-3.4) When a parent sender is connected to a receiver rcvr, any receiver used to connect a child sender has an associated environment equal to *FWD-ENV*(get_env(rcvr))[.](#exec.adapt.general-3.4.sentence-1) - [(3.5)](#exec.adapt.general-3.5) An adaptor whose child senders are all non-dependent ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) is itself non-dependent[.](#exec.adapt.general-3.5.sentence-1) - [(3.6)](#exec.adapt.general-3.6) These requirements apply to any function that is selected by the implementation of the sender adaptor[.](#exec.adapt.general-3.6.sentence-1) - [(3.7)](#exec.adapt.general-3.7) *Recommended practice*: Implementations should use the completion signatures of the adaptors to communicate type errors to users and to propagate any such type errors from child senders[.](#exec.adapt.general-3.7.sentence-1) [4](#exec.adapt.general-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3043) If a sender returned from a sender adaptor specified in [[exec.adapt]](#exec.adapt "33.9.12 Sender adaptors") is specified to include set_error_t(Err) among its set of completion signatures where decay_t denotes the type exception_ptr, but the implementation does not potentially evaluate an error completion operation with an exception_ptr argument, the implementation is allowed to omit the exception_ptr error completion signature from the set[.](#exec.adapt.general-4.sentence-1) #### [33.9.12.2](#exec.adapt.obj) Closure objects [[exec.adapt.obj]](exec.adapt.obj) [1](#exec.adapt.obj-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3055) A [*pipeable sender adaptor closure object*](#def:sender_adaptor_closure_object,pipeable "33.9.12.2 Closure objects [exec.adapt.obj]") is a function object that accepts one or more [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") arguments and returns a [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.adapt.obj-1.sentence-1) For a pipeable sender adaptor closure object c and an expression sndr such that decltype((sndr)) models [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), the following expressions are equivalent and yield a [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"):c(sndr) sndr | c Given an additional pipeable sender adaptor closure object d, the expression c | d produces another pipeable sender adaptor closure object e: e is a perfect forwarding call wrapper ([[func.require]](func.require "22.10.4 Requirements")) with the following properties: - [(1.1)](#exec.adapt.obj-1.1) Its target object is an object d2 of type decltype(auto(d)) direct-non-list-initialized with d[.](#exec.adapt.obj-1.1.sentence-1) - [(1.2)](#exec.adapt.obj-1.2) It has one bound argument entity, an object c2 of type decltype(auto(c)) direct-non-list-initialized with c[.](#exec.adapt.obj-1.2.sentence-1) - [(1.3)](#exec.adapt.obj-1.3) Its call pattern is d2(c2(arg)), where arg is the argument used in a function call expression of e[.](#exec.adapt.obj-1.3.sentence-1) The expression c | d is well-formed if and only if the initializations of the state entities ([[func.def]](func.def "22.10.3 Definitions")) of e are all well-formed[.](#exec.adapt.obj-1.sentence-5) [2](#exec.adapt.obj-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3088) An object t of type T is a pipeable sender adaptor closure object if T models [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]")>,T has no other base classes of type sender_adaptor_closure for any other type U, andT does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.adapt.obj-2.sentence-1) [3](#exec.adapt.obj-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3096) The template parameter D for sender_adaptor_closure can be an incomplete type[.](#exec.adapt.obj-3.sentence-1) Before any expression of type cv D appears as an operand to the | operator,D shall be complete and model [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]")>[.](#exec.adapt.obj-3.sentence-2) The behavior of an expression involving an object of type cv D as an operand to the | operator is undefined if overload resolution selects a program-defined operator| function[.](#exec.adapt.obj-3.sentence-3) [4](#exec.adapt.obj-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3107) A [*pipeable sender adaptor object*](#def:sender_adaptor_object,pipeable "33.9.12.2 Closure objects [exec.adapt.obj]") is a customization point object that accepts a [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") as its first argument and returns a [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.adapt.obj-4.sentence-1) If a pipeable sender adaptor object accepts only one argument, then it is a pipeable sender adaptor closure object[.](#exec.adapt.obj-4.sentence-2) [5](#exec.adapt.obj-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3114) If a pipeable sender adaptor object adaptor accepts more than one argument, then let sndr be an expression such that decltype((sndr)) models [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), let args... be arguments such that adaptor(sndr, args...) is a well-formed expression as specified below, and let BoundArgs be a pack that denotes decltype(auto(args))...[.](#exec.adapt.obj-5.sentence-1) The expression adaptor(args...) produces a pipeable sender adaptor closure object f that is a perfect forwarding call wrapper with the following properties: - [(5.1)](#exec.adapt.obj-5.1) Its target object is a copy of adaptor[.](#exec.adapt.obj-5.1.sentence-1) - [(5.2)](#exec.adapt.obj-5.2) Its bound argument entities bound_args consist of objects of types BoundArgs... direct-non-list-initialized withstd​::​forward(args)..., respectively[.](#exec.adapt.obj-5.2.sentence-1) - [(5.3)](#exec.adapt.obj-5.3) Its call pattern is adaptor(rcvr, bound_args...), where rcvr is the argument used in a function call expression of f[.](#exec.adapt.obj-5.3.sentence-1) The expression adaptor(args...) is well-formed if and only if the initializations of the bound argument entities of the result, as specified above, are all well-formed[.](#exec.adapt.obj-5.sentence-3) #### [33.9.12.3](#exec.write.env) execution​::​write_env [[exec.write.env]](exec.write.env) [1](#exec.write.env-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3143) write_env is a sender adaptor that accepts a sender and a queryable object, and that returns a sender that, when connected with a receiver rcvr, connects the adapted sender with a receiver whose execution environment is the result of joining the [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") object to the result of get_env(rcvr)[.](#exec.write.env-1.sentence-1) [2](#exec.write.env-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3153) write_env is a customization point object[.](#exec.write.env-2.sentence-1) For some subexpressions sndr and env, if decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") or if decltype((env)) does not satisfy [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]"), the expression write_env(sndr, env) is ill-formed[.](#exec.write.env-2.sentence-2) Otherwise, it is expression-equivalent to*make-sender*(write_env, env, sndr)[.](#exec.write.env-2.sentence-3) [3](#exec.write.env-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3162) Let *write-env-t* denote the type decltype(auto(write_env))[.](#exec.write.env-3.sentence-1) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for *write-env-t* as follows: [🔗](#lib:impls-for%3cwrite-env-t%3e) template<>struct *impls-for*<*write-env-t*> : *default-impls* {static constexpr auto *join-env*(const auto& state, const auto& env) noexcept {return *see below*; }static constexpr auto *get-env* =[](auto, const auto& state, const auto& rcvr) noexcept {return *join-env*(state, *FWD-ENV*(get_env(rcvr))); }; templatestatic consteval void *check-types*();}; [4](#exec.write.env-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3184) Invocation of*impls-for*<*write-env-t*>​::​*join-env* returns an object e such that - [(4.1)](#exec.write.env-4.1) decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") and - [(4.2)](#exec.write.env-4.2) given a query object q, the expression e.query(q) is expression-equivalent to state.query(q) if that expression is valid, otherwise, e.query(q) is expression-equivalent to env.query(q)[.](#exec.write.env-4.sentence-1) [5](#exec.write.env-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3199) For a type Sndr and a pack of types Env, let State be *data-type* and let JoinEnv be the packdecltype(*join-env*(declval(), *FWD-ENV*(declval())))[.](#exec.write.env-5.sentence-1) Then *impls-for*<*write-env-​t*>​::​*check-types*() is expression-equivalent toget_completion_signatures<*child- type*, JoinEnv...>()[.](#exec.write.env-5.sentence-2) #### [33.9.12.4](#exec.unstoppable) execution​::​unstoppable [[exec.unstoppable]](exec.unstoppable) [1](#exec.unstoppable-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3210) unstoppable is a sender adaptor that connects its inner sender with a receiver that has the execution environment of the outer receiver but with an object of type never_stop_token as the result of the get_stop_token query[.](#exec.unstoppable-1.sentence-1) [2](#exec.unstoppable-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3217) For a subexpression sndr,unstoppable(sndr) is expression-equivalent towrite_env(sndr, prop(get_stop_token, never_stop_token{}))[.](#exec.unstoppable-2.sentence-1) #### [33.9.12.5](#exec.starts.on) execution​::​starts_on [[exec.starts.on]](exec.starts.on) [1](#exec.starts.on-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3224) starts_on adapts an input sender into a sender that will start on an execution agent belonging to a particular scheduler's associated execution resource[.](#exec.starts.on-1.sentence-1) [2](#exec.starts.on-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3229) The name starts_on denotes a customization point object[.](#exec.starts.on-2.sentence-1) For subexpressions sch and sndr, if decltype(( sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), ordecltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),starts_on(sch, sndr) is ill-formed[.](#exec.starts.on-2.sentence-2) [3](#exec.starts.on-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3236) Otherwise, the expression starts_on(sch, sndr) is expression-equivalent to:transform_sender(*query-with-default*(get_domain, sch, default_domain()), *make-sender*(starts_on, sch, sndr)) except that sch is evaluated only once[.](#exec.starts.on-3.sentence-1) [4](#exec.starts.on-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3246) Let out_sndr and env be subexpressions such that OutSndr is decltype((out_sndr))[.](#exec.starts.on-4.sentence-1) If [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") is false, then the expressions starts_on.transform_env(out_sndr, env) andstarts_on.transform_sender(out_sndr, env) are ill-formed; otherwise - [(4.1)](#exec.starts.on-4.1) starts_on.transform_env(out_sndr, env) is equivalent to:auto&& [_, sch, _] = out_sndr;return *JOIN-ENV*(*SCHED-ENV*(sch), *FWD-ENV*(env)); - [(4.2)](#exec.starts.on-4.2) starts_on.transform_sender(out_sndr, env) is equivalent to:auto&& [_, sch, sndr] = out_sndr;return let_value( schedule(sch), [sndr = std::forward_like(sndr)]() mutablenoexcept(is_nothrow_move_constructible_v>) {return std::move(sndr); }); [5](#exec.starts.on-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3272) Let out_sndr be a subexpression denoting a sender returned from starts_on(sch, sndr) or one equal to such, and let OutSndr be the type decltype((out_sndr))[.](#exec.starts.on-5.sentence-1) Let out_rcvr be a subexpression denoting a receiver that has an environment of type Env such that [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.starts.on-5.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.starts.on-5.sentence-3) Calling start(op) shall start sndr on an execution agent of the associated execution resource of sch[.](#exec.starts.on-5.sentence-4) If scheduling onto sch fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.starts.on-5.sentence-5) #### [33.9.12.6](#exec.continues.on) execution​::​continues_on [[exec.continues.on]](exec.continues.on) [1](#exec.continues.on-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3289) continues_on adapts a sender into one that completes on the specified scheduler[.](#exec.continues.on-1.sentence-1) [2](#exec.continues.on-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3293) The name continues_on denotes a pipeable sender adaptor object[.](#exec.continues.on-2.sentence-1) For subexpressions sch and sndr, if decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), ordecltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),continues_on(sndr, sch) is ill-formed[.](#exec.continues.on-2.sentence-2) [3](#exec.continues.on-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3300) Otherwise, the expression continues_on(sndr, sch) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(continues_on, sch, sndr)) except that sndr is evaluated only once[.](#exec.continues.on-3.sentence-1) [4](#exec.continues.on-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3308) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for continues_on_t as follows:namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *get-attrs* =[](const auto& data, const auto& child) noexcept -> decltype(auto) {return *JOIN-ENV*(*SCHED-ATTRS*(data), *FWD-ENV*(get_env(child))); }; };} [5](#exec.continues.on-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3323) Let sndr and env be subexpressions such that Sndr is decltype((sndr))[.](#exec.continues.on-5.sentence-1) If [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") is false, then the expression continues_on.transform_sender(sndr, env) is ill-formed; otherwise, it is equal to:auto [_, data, child] = sndr;return schedule_from(std::move(data), std::move(child)); [*Note [1](#exec.continues.on-note-1)*: This causes the continues_on(sndr, sch) sender to becomeschedule_from(sch, sndr) when it is connected with a receiver whose execution domain does not customize continues_on[.](#exec.continues.on-5.sentence-2) — *end note*] [6](#exec.continues.on-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3340) Let out_sndr be a subexpression denoting a sender returned from continues_on(sndr, sch) or one equal to such, and let OutSndr be the type decltype((out_sndr))[.](#exec.continues.on-6.sentence-1) Let out_rcvr be a subexpression denoting a receiver that has an environment of type Env such that [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.continues.on-6.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.continues.on-6.sentence-3) Calling start(op) shall start sndr on the current execution agent and execute completion operations on out_rcvr on an execution agent of the execution resource associated with sch[.](#exec.continues.on-6.sentence-4) If scheduling onto sch fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.continues.on-6.sentence-5) #### [33.9.12.7](#exec.schedule.from) execution​::​schedule_from [[exec.schedule.from]](exec.schedule.from) [1](#exec.schedule.from-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3359) schedule_from schedules work dependent on the completion of a sender onto a scheduler's associated execution resource[.](#exec.schedule.from-1.sentence-1) [*Note [1](#exec.schedule.from-note-1)*: schedule_from is not meant to be used in user code; it is used in the implementation of continues_on[.](#exec.schedule.from-1.sentence-2) — *end note*] [2](#exec.schedule.from-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3367) The name schedule_from denotes a customization point object[.](#exec.schedule.from-2.sentence-1) For some subexpressions sch and sndr, let Sch be decltype((sch)) andSndr be decltype((sndr))[.](#exec.schedule.from-2.sentence-2) If Sch does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), orSndr does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),schedule_from(sch, sndr) is ill-formed[.](#exec.schedule.from-2.sentence-3) [3](#exec.schedule.from-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3376) Otherwise, the expression schedule_from(sch, sndr) is expression-equivalent to:transform_sender(*query-with-default*(get_domain, sch, default_domain()), *make-sender*(schedule_from, sch, sndr)) except that sch is evaluated only once[.](#exec.schedule.from-3.sentence-1) [4](#exec.schedule.from-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3386) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for schedule_from_t as follows: [🔗](#lib:impls-for%3cschedule_from_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *get-attrs* = *see below*; static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; templatestatic consteval void *check-types*(); };} [5](#exec.schedule.from-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3404) The member *impls-for*​::​*get-attrs* is initialized with a callable object equivalent to the following lambda:[](const auto& data, const auto& child) noexcept -> decltype(auto) {return *JOIN-ENV*(*SCHED-ATTRS*(data), *FWD-ENV*(get_env(child)));} [6](#exec.schedule.from-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3413) The member *impls-for*​::​*get-state* is initialized with a callable object equivalent to the following lambda:[](Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*)requires [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<*child-type*, *FWD-ENV-T*(env_of_t)> {auto& [_, sch, child] = sndr; using sched_t = decltype(auto(sch)); using variant_t = *see below*; using receiver_t = *see below*; using operation_t = connect_result_t, receiver_t>; constexpr bool nothrow = noexcept(connect(schedule(sch), receiver_t{nullptr})); struct *state-type* { Rcvr& *rcvr*; // *exposition only* variant_t *async-result*; // *exposition only* operation_t *op-state*; // *exposition only*explicit *state-type*(sched_t sch, Rcvr& rcvr) noexcept(nothrow): *rcvr*(rcvr), *op-state*(connect(schedule(sch), receiver_t{this})) {}}; return *state-type*{sch, rcvr};} [🔗](#lib:check-types,impls-for%3cschedule_from_t%3e) `template static consteval void check-types(); ` [7](#exec.schedule.from-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3448) *Effects*: Equivalent to:get_completion_signatures>, *FWD-ENV-T*(Env)...>();auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>();*decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](#expos "33.9.2 Exposition-only entities") [8](#exec.schedule.from-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3458) Objects of the local class *state-type* can be used to initialize a structured binding[.](#exec.schedule.from-8.sentence-1) [9](#exec.schedule.from-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3462) Let Sigs be a pack of the arguments to the completion_signatures specialization named by completion_signatures_of_t<*child-type*, *FWD-ENV-T*(env_of_t)>[.](#exec.schedule.from-9.sentence-1) Let *as-tuple* be an alias template such that*as-tuple* denotes the type *decayed-tuple*, and let *is-nothrow-decay-copy-sig* be a variable template such thatauto(*is-nothrow-decay-copy-sig*) is a constant expression of type bool and equal to (is_nothrow_constructible_v, Args> && ...)[.](#exec.schedule.from-9.sentence-2) Let *error-completion* be a pack consisting of the type set_error_t(exception_ptr) if (*is-nothrow-decay-copy-sig* &&...) is false, and an empty pack otherwise[.](#exec.schedule.from-9.sentence-3) Then variant_t denotes the type variant..., *error-completion*...>, except with duplicate types removed[.](#exec.schedule.from-9.sentence-4) [10](#exec.schedule.from-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3481) receiver_t is an alias for the following exposition-only class:namespace std::execution {struct *receiver-type* {using receiver_concept = receiver_t; *state-type** *state*; // *exposition only*void set_value() && noexcept { visit([this](Tuple& result) noexcept -> void {if constexpr (![same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {auto& [tag, ...args] = result; tag(std::move(*state*->*rcvr*), std::move(args)...); }}, *state*->*async-result*); }templatevoid set_error(Error&& err) && noexcept { execution::set_error(std::move(*state*->*rcvr*), std::forward(err)); }void set_stopped() && noexcept { execution::set_stopped(std::move(*state*->*rcvr*)); }decltype(auto) get_env() const noexcept {return *FWD-ENV*(execution::get_env(*state*->*rcvr*)); }};} [11](#exec.schedule.from-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3516) The expression in the noexcept clause of the lambda is true if the construction of the returned *state-type* object is not potentially throwing; otherwise, false[.](#exec.schedule.from-11.sentence-1) [12](#exec.schedule.from-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3522) The member *impls-for*​::​*complete* is initialized with a callable object equivalent to the following lambda:[](auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept-> void {using result_t = *decayed-tuple*; constexpr bool nothrow = (is_nothrow_constructible_v, Args> && ...); try { state.*async-result*.template emplace(Tag(), std::forward(args)...); } catch (...) {if constexpr (!nothrow) state.*async-result*.template emplace>(set_error, current_exception()); } start(state.*op-state*);}; [13](#exec.schedule.from-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3542) Let out_sndr be a subexpression denoting a sender returned from schedule_from(sch, sndr) or one equal to such, and let OutSndr be the type decltype((out_sndr))[.](#exec.schedule.from-13.sentence-1) Let out_rcvr be a subexpression denoting a receiver that has an environment of type Env such that [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.schedule.from-13.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.schedule.from-13.sentence-3) Calling start(op) shall start sndr on the current execution agent and execute completion operations on out_rcvr on an execution agent of the execution resource associated with sch[.](#exec.schedule.from-13.sentence-4) If scheduling onto sch fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.schedule.from-13.sentence-5) #### [33.9.12.8](#exec.on) execution​::​on [[exec.on]](exec.on) [1](#exec.on-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3561) The on sender adaptor has two forms: - [(1.1)](#exec.on-1.1) on(sch, sndr), which starts a sender sndr on an execution agent belonging to a scheduler sch's associated execution resource and that, upon sndr's completion, transfers execution back to the execution resource on which the on sender was started[.](#exec.on-1.1.sentence-1) - [(1.2)](#exec.on-1.2) on(sndr, sch, closure), which upon completion of a sender sndr, transfers execution to an execution agent belonging to a scheduler sch's associated execution resource, then executes a sender adaptor closure closure with the async results of the sender, and that then transfers execution back to the execution resource on which sndr completed[.](#exec.on-1.2.sentence-1) [2](#exec.on-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3582) The name on denotes a pipeable sender adaptor object[.](#exec.on-2.sentence-1) For subexpressions sch and sndr,on(sch, sndr) is ill-formed if any of the following is true: - [(2.1)](#exec.on-2.1) decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), or - [(2.2)](#exec.on-2.2) decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") andsndr is not a pipeable sender adaptor closure object ([[exec.adapt.obj]](#exec.adapt.obj "33.9.12.2 Closure objects")), or - [(2.3)](#exec.on-2.3) decltype((sndr)) satisfies [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") andsndr is also a pipeable sender adaptor closure object[.](#exec.on-2.sentence-2) [3](#exec.on-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3598) Otherwise, if decltype((sndr)) satisfies [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), the expression on(sch, sndr) is expression-equivalent to:transform_sender(*query-with-default*(get_domain, sch, default_domain()), *make-sender*(on, sch, sndr)) except that sch is evaluated only once[.](#exec.on-3.sentence-1) [4](#exec.on-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3608) For subexpressions sndr, sch, and closure, if - [(4.1)](#exec.on-4.1) decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), or - [(4.2)](#exec.on-4.2) decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), or - [(4.3)](#exec.on-4.3) closure is not a pipeable sender adaptor closure object ([[exec.adapt.obj]](#exec.adapt.obj "33.9.12.2 Closure objects")), the expression on(sndr, sch, closure) is ill-formed; otherwise, it is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(on, *product-type*{sch, closure}, sndr)) except that sndr is evaluated only once[.](#exec.on-4.sentence-1) [5](#exec.on-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3627) Let out_sndr and env be subexpressions, let OutSndr be decltype((out_sndr)), and let Env be decltype((env))[.](#exec.on-5.sentence-1) If [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") is false, then the expressions on.transform_env(out_sndr, env) andon.transform_sender(out_sndr, env) are ill-formed[.](#exec.on-5.sentence-2) [6](#exec.on-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3635) Otherwise: Let *not-a-scheduler* be an unspecified empty class type[.](#exec.on-6.sentence-1) [7](#exec.on-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3639) The expression on.transform_env(out_sndr, env) has effects equivalent to:auto&& [_, data, _] = out_sndr;if constexpr ([scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]")) {return *JOIN-ENV*(*SCHED-ENV*(std::forward_like(data)), *FWD-ENV*(std::forward(env)));} else {return std::forward(env);} [8](#exec.on-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3651) The expression on.transform_sender(out_sndr, env) has effects equivalent to:auto&& [_, data, child] = out_sndr;if constexpr ([scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]")) {auto orig_sch =*query-with-default*(get_scheduler, env, *not-a-scheduler*()); if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {return *not-a-sender*{}; } else {return continues_on( starts_on(std::forward_like(data), std::forward_like(child)), std::move(orig_sch)); }} else {auto& [sch, closure] = data; auto orig_sch = *query-with-default*( get_completion_scheduler, get_env(child), *query-with-default*(get_scheduler, env, *not-a-scheduler*())); if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {return *not-a-sender*{}; } else {return write_env( continues_on( std::forward_like(closure)( continues_on( write_env(std::forward_like(child), *SCHED-ENV*(orig_sch)), sch)), orig_sch), *SCHED-ENV*(sch)); }} [9](#exec.on-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3689) Let out_sndr be a subexpression denoting a sender returned from on(sch, sndr) or one equal to such, and let OutSndr be the type decltype((out_sndr))[.](#exec.on-9.sentence-1) Let out_rcvr be a subexpression denoting a receiver that has an environment of type Env such that [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.on-9.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.on-9.sentence-3) Calling start(op) shall - [(9.1)](#exec.on-9.1) remember the current scheduler, get_scheduler(get_env(rcvr)); - [(9.2)](#exec.on-9.2) start sndr on an execution agent belonging tosch's associated execution resource; - [(9.3)](#exec.on-9.3) upon sndr's completion, transfer execution back to the execution resource associated with the scheduler remembered in step 1; and - [(9.4)](#exec.on-9.4) forward sndr's async result to out_rcvr[.](#exec.on-9.sentence-4) If any scheduling operation fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.on-9.sentence-5) [10](#exec.on-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3716) Let out_sndr be a subexpression denoting a sender returned from on(sndr, sch, closure) or one equal to such, and let OutSndr be the type decltype((out_sndr))[.](#exec.on-10.sentence-1) Let out_rcvr be a subexpression denoting a receiver that has an environment of type Env such that [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.on-10.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.on-10.sentence-3) Calling start(op) shall - [(10.1)](#exec.on-10.1) remember the current scheduler, which is the first of the following expressions that is well-formed: * [(10.1.1)](#exec.on-10.1.1) get_completion_scheduler(get_env(sndr)) * [(10.1.2)](#exec.on-10.1.2) get_scheduler(get_env(rcvr)); - [(10.2)](#exec.on-10.2) start sndr on the current execution agent; - [(10.3)](#exec.on-10.3) upon sndr's completion, transfer execution to an agent owned by sch's associated execution resource; - [(10.4)](#exec.on-10.4) forward sndr's async result as if by connecting and starting a sender closure(S), where S is a sender that completes synchronously with sndr's async result; and - [(10.5)](#exec.on-10.5) upon completion of the operation started in the previous step, transfer execution back to the execution resource associated with the scheduler remembered in step 1 and forward the operation's async result to out_rcvr[.](#exec.on-10.sentence-4) If any scheduling operation fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.on-10.sentence-5) #### [33.9.12.9](#exec.then) execution​::​then, execution​::​upon_error, execution​::​upon_stopped [[exec.then]](exec.then) [1](#exec.then-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3757) then attaches an invocable as a continuation for an input sender's value completion operation[.](#exec.then-1.sentence-1) upon_error and upon_stopped do the same for the error and stopped completion operations, respectively, sending the result of the invocable as a value completion[.](#exec.then-1.sentence-2) [2](#exec.then-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3764) The names then, upon_error, and upon_stopped denote pipeable sender adaptor objects[.](#exec.then-2.sentence-1) Let the expression *then-cpo* be one ofthen, upon_error, or upon_stopped[.](#exec.then-2.sentence-2) For subexpressions sndr and f, if decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), ordecltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]"),*then-cpo*(sndr, f) is ill-formed[.](#exec.then-2.sentence-3) [3](#exec.then-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3774) Otherwise, the expression *then-cpo*(sndr, f) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(*then-cpo*, f, sndr)) except that sndr is evaluated only once[.](#exec.then-3.sentence-1) [4](#exec.then-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3782) For then, upon_error, and upon_stopped, let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#exec.then-4.sentence-1) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for *then-cpo* as follows: [🔗](#lib:impls-for%3cdecayed-typeof%3cthen-cpo%3e%3e) namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<*then-cpo*>> : *default-impls* {static constexpr auto *complete* =[](auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")>) {*TRY-SET-VALUE*(rcvr, invoke(std::move(fn), std::forward(args)...)); } else { Tag()(std::move(rcvr), std::forward(args)...); }}; templatestatic consteval void *check-types*(); };} [🔗](#lib:check-types,impls-for%3cdecayed-typeof%3cthen-cpo%3e%3e) `template static consteval void check-types(); ` [5](#exec.then-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3817) *Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>();auto fn = [](set_value_t(*)(Ts...)) {if constexpr (![invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")>, Ts...>)throw *unspecified-exception*();}; cs.*for-each*(*overload-set*{fn, [](auto){}}); where *unspecified-exception* is a type derived from exception[.](#exec.then-5.sentence-1) [6](#exec.then-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3832) The expression *then-cpo*(sndr, f) has undefined behavior unless it returns a sender out_sndr that - [(6.1)](#exec.then-6.1) invokes f or a copy of such with the value, error, or stopped result datums of sndr for then, upon_error, and upon_stopped, respectively, using the result value of f as out_sndr's value completion, and - [(6.2)](#exec.then-6.2) forwards all other completion operations unchanged[.](#exec.then-6.sentence-1) #### [33.9.12.10](#exec.let) execution​::​let_value, execution​::​let_error, execution​::​let_stopped [[exec.let]](exec.let) [1](#exec.let-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3847) let_value, let_error, and let_stopped transform a sender's value, error, and stopped completions, respectively, into a new child asynchronous operation by passing the sender's result datums to a user-specified callable, which returns a new sender that is connected and started[.](#exec.let-1.sentence-1) [2](#exec.let-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3854) For let_value, let_error, and let_stopped, let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#exec.let-2.sentence-1) Let the expression *let-cpo* be one oflet_value, let_error, or let_stopped[.](#exec.let-2.sentence-2) For a subexpression sndr, let *let-env*(sndr) be expression-equivalent to the first well-formed expression below: - [(2.1)](#exec.let-2.1) *SCHED-ENV*(get_completion_scheduler<*decayed-typeof*<*set-cpo*>>(get_env(sndr))) - [(2.2)](#exec.let-2.2) *MAKE-ENV*(get_domain, get_domain(get_env(sndr))) - [(2.3)](#exec.let-2.3) (void(sndr), env<>{}) [3](#exec.let-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3872) The names let_value, let_error, and let_stopped denote pipeable sender adaptor objects[.](#exec.let-3.sentence-1) For subexpressions sndr and f, let F be the decayed type of f[.](#exec.let-3.sentence-2) If decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") or if decltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]"), the expression *let-cpo*(sndr, f) is ill-formed[.](#exec.let-3.sentence-3) If F does not satisfy [invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]"), the expression let_stopped(sndr, f) is ill-formed[.](#exec.let-3.sentence-4) [4](#exec.let-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3883) Otherwise, the expression *let-cpo*(sndr, f) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(*let-cpo*, f, sndr)) except that sndr is evaluated only once[.](#exec.let-4.sentence-1) [5](#exec.let-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3891) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for *let-cpo* as follows: [🔗](#lib:impls-for%3cdecayed-typeof%3clet-cpo%3e%3e) namespace std::execution {templatevoid *let-bind*(State& state, Rcvr& rcvr, Args&&... args); // *exposition only*template<>struct *impls-for*<*decayed-typeof*<*let-cpo*>> : *default-impls* {static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; templatestatic consteval void *check-types*(); };} [6](#exec.let-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3911) Let *receiver2* denote the following exposition-only class template:namespace std::execution {templatestruct *receiver2* {using receiver_concept = receiver_t; templatevoid set_value(Args&&... args) && noexcept { execution::set_value(std::move(*rcvr*), std::forward(args)...); }templatevoid set_error(Error&& err) && noexcept { execution::set_error(std::move(*rcvr*), std::forward(err)); }void set_stopped() && noexcept { execution::set_stopped(std::move(*rcvr*)); }decltype(auto) get_env() const noexcept {return *see below*; } Rcvr& *rcvr*; // *exposition only* Env *env*; // *exposition only*};} Invocation of the function *receiver2*​::​get_env returns an object e such that - [(6.1)](#exec.let-6.1) decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") and - [(6.2)](#exec.let-6.2) given a query object q, the expression e.query(q) is expression-equivalent to *env*.query(q) if that expression is valid; otherwise, if the type of q satisfies [*forwarding-query*](execution.syn#concept:forwarding-query "33.4 Header synopsis [execution.syn]"),e.query(q) is expression-equivalent to get_env(*rcvr*).query(q); otherwise,e.query(q) is ill-formed[.](#exec.let-6.sentence-2) [🔗](#lib:check-types,impls-for%3cdecayed-typeof%3clet-cpo%3e%3e) `template static consteval void check-types(); ` [7](#exec.let-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3966) *Effects*: Equivalent to:using LetFn = remove_cvref_t<*data-type*>;auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>();auto fn = [](*decayed-typeof*<*set-cpo*>(*)(Ts...)) {if constexpr (!*is-valid-let-sender*) // *see below*throw *unspecified-exception*();}; cs.*for-each*(*overload-set*(fn, [](auto){})); where *unspecified-exception* is a type derived from exception, and where *is-valid-let-sender* is true if and only if all of the following are true: - [(7.1)](#exec.let-7.1) ([constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_­from [concept.constructible]"), Ts> &&...) - [(7.2)](#exec.let-7.2) [invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")&...> - [(7.3)](#exec.let-7.3) [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")&...>> - [(7.4)](#exec.let-7.4) sizeof...(Env) == 0 || [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")&...>, *env-t* ...> where *env-t* is the packdecltype(*let-cpo*.transform_env(declval(), declval()))[.](#exec.let-7.sentence-1) [8](#exec.let-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3993) *impls-for*<*decayed-typeof*<*let-cpo*>>​::​*get-state* is initialized with a callable object equivalent to the following:[](Sndr&& sndr, Rcvr& rcvr) requires *see below* {auto& [_, fn, child] = sndr; using fn_t = decay_t; using env_t = decltype(*let-env*(child)); using args_variant_t = *see below*; using ops2_variant_t = *see below*; struct *state-type* { fn_t *fn*; // *exposition only* env_t *env*; // *exposition only* args_variant_t *args*; // *exposition only* ops2_variant_t *ops2*; // *exposition only*}; return *state-type*{*allocator-aware-forward*(std::forward_like(fn), rcvr), *let-env*(child), {}, {}};} [9](#exec.let-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4015) Let Sigs be a pack of the arguments to the completion_signatures specialization named bycompletion_signatures_of_t<*child-type*, *FWD-ENV-T*(env_of_t)>[.](#exec.let-9.sentence-1) Let LetSigs be a pack of those types in Sigs with a return type of *decayed-typeof*<*set-cpo*>[.](#exec.let-9.sentence-2) Let *as-tuple* be an alias template such that *as-tuple* denotes the type *decayed-tuple*[.](#exec.let-9.sentence-3) Then args_variant_t denotes the type variant...> except with duplicate types removed[.](#exec.let-9.sentence-4) [10](#exec.let-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4028) Given a type Tag and a pack Args, let *as-sndr2* be an alias template such that *as-sndr2* denotes the type *call-result-t*&...>[.](#exec.let-10.sentence-1) Then ops2_variant_t denotes the typevariant, *receiver2*>...> except with duplicate types removed[.](#exec.let-10.sentence-2) [11](#exec.let-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4040) The [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") constraining the above lambda is satisfied if and only if the types args_variant_t and ops2_variant_t are well-formed[.](#exec.let-11.sentence-1) [12](#exec.let-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4045) The exposition-only function template *let-bind* has effects equivalent to:using args_t = *decayed-tuple*;auto mkop2 = [&] {return connect( apply(std::move(state.fn), state.args.template emplace(std::forward(args)...)), *receiver2*{rcvr, std::move(state.env)});}; start(state.ops2.template emplace(*emplace-from*{mkop2})); [13](#exec.let-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4059) *impls-for*<*decayed-typeof*>​::​*complete* is initialized with a callable object equivalent to the following:[](auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")>) {*TRY-EVAL*(rcvr, *let-bind*(state, rcvr, std::forward(args)...)); } else { Tag()(std::move(rcvr), std::forward(args)...); }} [14](#exec.let-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4073) Let sndr and env be subexpressions, and let Sndr be decltype((sndr))[.](#exec.let-14.sentence-1) If[*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")> is false, then the expression *let-cpo*.transform_env(sndr, env) is ill-formed[.](#exec.let-14.sentence-2) Otherwise, it is equal to:auto& [_, _, child] = sndr;return *JOIN-ENV*(*let-env*(child), *FWD-ENV*(env)); [15](#exec.let-15) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4087) Let the subexpression out_sndr denote the result of the invocation *let-cpo*(sndr, f) or an object equal to such, and let the subexpression rcvr denote a receiver such that the expression connect(out_sndr, rcvr) is well-formed[.](#exec.let-15.sentence-1) The expression connect(out_sndr, rcvr) has undefined behavior unless it creates an asynchronous operation ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) that, when started: - [(15.1)](#exec.let-15.1) invokes f when *set-cpo* is called with sndr's result datums, - [(15.2)](#exec.let-15.2) makes its completion dependent on the completion of a sender returned by f, and - [(15.3)](#exec.let-15.3) propagates the other completion operations sent by sndr[.](#exec.let-15.sentence-2) #### [33.9.12.11](#exec.bulk) execution​::​bulk, execution​::​bulk_chunked, and execution​::​bulk_unchunked [[exec.bulk]](exec.bulk) [1](#exec.bulk-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4109) bulk, bulk_chunked, and bulk_unchunked run a task repeatedly for every index in an index space[.](#exec.bulk-1.sentence-1) [2](#exec.bulk-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4113) The names bulk, bulk_chunked, and bulk_unchunked denote pipeable sender adaptor objects[.](#exec.bulk-2.sentence-1) Let *bulk-algo* be eitherbulk, bulk_chunked, or bulk_unchunked[.](#exec.bulk-2.sentence-2) For subexpressions sndr, policy, shape, and f, letPolicy be remove_cvref_t,Shape be decltype(auto(shape)), andFunc be decay_t[.](#exec.bulk-2.sentence-3) If - [(2.1)](#exec.bulk-2.1) decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), or - [(2.2)](#exec.bulk-2.2) is_execution_policy_v is false, or - [(2.3)](#exec.bulk-2.3) Shape does not satisfy [integral](concepts.arithmetic#concept:integral "18.4.7 Arithmetic concepts [concepts.arithmetic]"), or - [(2.4)](#exec.bulk-2.4) Func does not model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]"), *bulk-algo*(sndr, policy, shape, f) is ill-formed[.](#exec.bulk-2.sentence-4) [3](#exec.bulk-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4136) Otherwise, the expression *bulk-algo*(sndr, policy, shape, f) is expression-equivalent to: transform_sender(*get-domain-early*(sndr), *make-sender*(*bulk-algo*, *product-type*<*see below*, Shape, Func>{policy, shape, f}, sndr)) except that sndr is evaluated only once[.](#exec.bulk-3.sentence-2) The first template argument of *product-type* is Policy if Policy models [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]"), andconst Policy& otherwise[.](#exec.bulk-3.sentence-3) [4](#exec.bulk-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4150) Let sndr and env be subexpressions such thatSndr is decltype((sndr))[.](#exec.bulk-4.sentence-1) If [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") is false, then the expression bulk.transform_sender(sndr, env) is ill-formed; otherwise, it is equivalent to:auto [_, data, child] = sndr;auto& [policy, shape, f] = data;auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)noexcept(noexcept(f(begin, vs...))) {while (begin != end) func(begin++, vs...);}return bulk_chunked(std::move(child), policy, shape, std::move(new_f)); [*Note [1](#exec.bulk-note-1)*: This causes the bulk(sndr, policy, shape, f) sender to be expressed in terms of bulk_chunked(sndr, policy, shape, f) when it is connected to a receiver whose execution domain does not customize bulk[.](#exec.bulk-4.sentence-2) — *end note*] [5](#exec.bulk-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4172) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for bulk_chunked_t as follows: [🔗](#lib:impls-for%3cbulk_chunked_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *complete* = *see below*; templatestatic consteval void *check-types*(); };} The member *impls-for*​::​*complete* is initialized with a callable object equivalent to the following lambda:[](Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {auto& [policy, shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) { f(static_cast(0), auto(shape), args...); Tag()(std::move(rcvr), std::forward(args)...); }()); } else { Tag()(std::move(rcvr), std::forward(args)...); }} The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") of the lambda above istrue if and only if Tag denotes a type other than set_value_t or if the expression f(auto(shape), auto(shape), args...) is well-formed[.](#exec.bulk-5.sentence-3) [6](#exec.bulk-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4210) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for bulk_unchunked_t as follows:namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *complete* = *see below*; };} The member *impls-for*​::​*complete* is initialized with a callable object equivalent to the following lambda:[](Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {auto& [shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) {for (decltype(auto(shape)) i = 0; i < shape; ++i) { f(auto(i), args...); } Tag()(std::move(rcvr), std::forward(args)...); }()); } else { Tag()(std::move(rcvr), std::forward(args)...); }} The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") of the lambda above is true if and only if Tag denotes a type other than set_value_t or if the expression f(auto(shape), args...) is well-formed[.](#exec.bulk-6.sentence-3) [🔗](#lib:check-types,impls-for%3cbulk_t%3e) `template static consteval void check-types(); ` [7](#exec.bulk-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4253) *Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>();auto fn = [](set_value_t(*)(Ts...)) {if constexpr (![invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")>, Ts&...>)throw *unspecified-exception*();}; cs.*for-each*(*overload-set*(fn, [](auto){})); where *unspecified-exception* is a type derived from exception[.](#exec.bulk-7.sentence-1) [8](#exec.bulk-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4268) Let the subexpression out_sndr denote the result of the invocation*bulk-algo*(sndr, policy, shape, f) or an object equal to such, and let the subexpression rcvr denote a receiver such that the expression connect(out_sndr, rcvr) is well-formed[.](#exec.bulk-8.sentence-1) The expression connect(out_sndr, rcvr) has undefined behavior unless it creates an asynchronous operation ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) that, when started: - [(8.1)](#exec.bulk-8.1) If sndr has a successful completion, whereargs is a pack of lvalue subexpressions referring to the value completion result datums of sndr, or decayed copies of those values if they model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]"), then: * [(8.1.1)](#exec.bulk-8.1.1) If out_sndr also completes successfully, then: + [(8.1.1.1)](#exec.bulk-8.1.1.1) for bulk, invokes f(i, args...) for every i of type Shape from 0 to shape; + [(8.1.1.2)](#exec.bulk-8.1.1.2) for bulk_unchunked, invokes f(i, args...) for every i of type Shape from 0 to shape; *Recommended practice*: The underlying scheduler should execute each iteration on a distinct execution agent[.](#exec.bulk-8.1.1.2.sentence-2) + [(8.1.1.3)](#exec.bulk-8.1.1.3) for bulk_chunked, invokes f(b, e, args...) zero or more times with pairs of b and e of type Shape in range [0, shape], such that b[.](#exec.when.all-2.sentence-2) Let CD2 be CD if CD is well-formed, anddefault_domain otherwise[.](#exec.when.all-2.sentence-3) The expressions when_all(sndrs...) andwhen_all_with_variant(sndrs...) are ill-formed if any of the following is true: - [(2.1)](#exec.when.all-2.1) sizeof...(sndrs) is 0, or - [(2.2)](#exec.when.all-2.2) ([sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") && ...) is false[.](#exec.when.all-2.sentence-4) [3](#exec.when.all-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4400) The expression when_all(sndrs...) is expression-equivalent to:transform_sender(CD2(), *make-sender*(when_all, {}, sndrs...)) [4](#exec.when.all-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4406) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for when_all_t as follows: [🔗](#lib:impls-for%3cwhen_all_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *get-attrs* = *see below*; static constexpr auto *get-env* = *see below*; static constexpr auto *get-state* = *see below*; static constexpr auto *start* = *see below*; static constexpr auto *complete* = *see below*; templatestatic consteval void *check-types*(); };} [5](#exec.when.all-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4426) Let *make-when-all-env* be the following exposition-only function template:templateconstexpr auto *make-when-all-env*(inplace_stop_source& stop_src, // *exposition only* Env&& env) noexcept {return *see below*;} Returns an object e such that - [(5.1)](#exec.when.all-5.1) decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]"), and - [(5.2)](#exec.when.all-5.2) e.query(get_stop_token) is expression-equivalent tostate.*stop-src*.get_token(), and - [(5.3)](#exec.when.all-5.3) given a query object q with type other than cv stop_token_t and whose type satisfies *forwarding-query*,e.query(q) is expression-equivalent to get_env(rcvr).query(q)[.](#exec.when.all-5.sentence-2) [6](#exec.when.all-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4452) Let *when-all-env* be an alias template such that*when-all-env* denotes the typedecltype(*make- when-all-env*(declval(), declval()))[.](#exec.when.all-6.sentence-1) [🔗](#lib:check-types,impls-for%3cwhen_all_t%3e) `template static consteval void check-types(); ` [7](#exec.when.all-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4464) Let Is be the pack of integral template arguments of the integer_sequence specialization denoted by*indices-for*[.](#exec.when.all-7.sentence-1) [8](#exec.when.all-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4469) *Effects*: Equivalent to:auto fn = []() {auto cs = get_completion_signatures...>(); if constexpr (cs.*count-of*(set_value) >= 2)throw *unspecified-exception*(); *decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")};(fn.template operator()<*child-type*>(), ...); where *unspecified-exception* is a type derived from exception[.](#exec.when.all-8.sentence-1) [9](#exec.when.all-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4484) *Throws*: Any exception thrown as a result of evaluating the *Effects*, or an exception of an unspecified type derived from exception when CD is ill-formed[.](#exec.when.all-9.sentence-1) [10](#exec.when.all-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4491) The member *impls-for*​::​*get-attrs* is initialized with a callable object equivalent to the following lambda expression:[](auto&&, auto&&... child) noexcept {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {return env<>(); } else {return *MAKE-ENV*(get_domain, CD()); }} [11](#exec.when.all-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4505) The member *impls-for*​::​*get-env* is initialized with a callable object equivalent to the following lambda expression:[](auto&&, State& state, const Receiver& rcvr) noexcept {return *make-when-all-env*(state.*stop-src*, get_env(rcvr));} [12](#exec.when.all-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4515) The member *impls-for*​::​*get-state* is initialized with a callable object equivalent to the following lambda expression:[](Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {return e;} where e is the expressionstd::forward(sndr).*apply*(*make-state*()) and where *make-state* is the following exposition-only class template:enum class *disposition* { *started*, *error*, *stopped* }; // *exposition only*templatestruct *make-state* {templateauto operator()(auto, auto, Sndrs&&... sndrs) const {using values_tuple = *see below*; using errors_variant = *see below*; using stop_callback = stop_callback_for_t>, *on-stop-request*>; struct *state-type* {void *arrive*(Rcvr& rcvr) noexcept { // *exposition only*if (0 == --count) {*complete*(rcvr); }}void *complete*(Rcvr& rcvr) noexcept; // *exposition only* atomic *count*{sizeof...(sndrs)}; // *exposition only* inplace_stop_source *stop_src*{}; // *exposition only* atomic<*disposition*> disp{*disposition*::*started*}; // *exposition only* errors_variant *errors*{}; // *exposition only* values_tuple *values*{}; // *exposition only* optional *on_stop*{nullopt}; // *exposition only*}; return *state-type*{}; }}; [13](#exec.when.all-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4562) Let *copy-fail* be exception_ptr if decay-copying any of the child senders' result datums can potentially throw; otherwise, *none-such*, where *none-such* is an unspecified empty class type[.](#exec.when.all-13.sentence-1) [14](#exec.when.all-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4568) The alias values_tuple denotes the typetuple), *decayed-tuple*, optional>...> if that type is well-formed; otherwise, tuple<>[.](#exec.when.all-14.sentence-1) [15](#exec.when.all-15) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4575) The alias errors_variant denotes the type variant<*none-such*, *copy-fail*, Es...> with duplicate types removed, where Es is the pack of the decayed types of all the child senders' possible error result datums[.](#exec.when.all-15.sentence-1) [16](#exec.when.all-16) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4582) The membervoid *state-type*​::​*complete*(Rcvr& rcvr) noexcept behaves as follows: - [(16.1)](#exec.when.all-16.1) If disp is equal to *disposition*​::​*started*, evaluates:auto tie = [](tuple& t) noexcept { return tuple(t); };auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); }; *on_stop*.reset(); apply([&](auto&... opts) noexcept { apply(set, tuple_cat(tie(*opts)...)); }, values); - [(16.2)](#exec.when.all-16.2) Otherwise, if disp is equal to *disposition*​::​*error*, evaluates:*on_stop*.reset(); visit([&](Error& error) noexcept {if constexpr (![same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) { set_error(std::move(rcvr), std::move(error)); }}, errors); - [(16.3)](#exec.when.all-16.3) Otherwise, evaluates:*on_stop*.reset(); set_stopped(std::move(rcvr)); [17](#exec.when.all-17) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4623) The member *impls-for*​::​*start* is initialized with a callable object equivalent to the following lambda expression:[]( State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void { state.*on_stop*.emplace( get_stop_token(get_env(rcvr)), *on-stop-request*{state.*stop_src*}); if (state.*stop_src*.stop_requested()) { state.*on_stop.*reset(); set_stopped(std::move(rcvr)); } else {(start(ops), ...); }} [18](#exec.when.all-18) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4642) The member *impls-for​::​*complete** is initialized with a callable object equivalent to the following lambda expression:[](this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {if (*disposition*::*error* != state.disp.exchange(*disposition*::*error*)) { state.*stop_src*.request_stop(); *TRY-EMPLACE-ERROR*(state.errors, std::forward(args)...); }} else if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {auto expected = *disposition*::*started*; if (state.disp.compare_exchange_strong(expected, *disposition*::*stopped*)) { state.*stop_src*.request_stop(); }} else if constexpr (![same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")>) {if (state.disp == *disposition*::*started*) {auto& opt = get(state.values); *TRY-EMPLACE-VALUE*(complete, opt, std::forward(args)...); }} state.*arrive*(rcvr);} where *TRY-EMPLACE-ERROR*(v, e), for subexpressions v and e, is equivalent to:try { v.template emplace(e);} catch (...) { v.template emplace(current_exception());} if the expression decltype(auto(e))(e) is potentially throwing; otherwise, v.template emplace(e); and where *TRY-EMPLACE-VALUE*(c, o, as...), for subexpressions c, o, and pack of subexpressions as, is equivalent to:try { o.emplace(as...);} catch (...) { c(Index(), state, rcvr, set_error, current_exception()); return;} if the expression *decayed-tuple*{as...} is potentially throwing; otherwise, o.emplace(as...)[.](#exec.when.all-18.sentence-1) [19](#exec.when.all-19) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4694) The expression when_all_with_variant(sndrs...) is expression-equivalent to:transform_sender(CD2(), *make-sender*(when_all_with_variant, {}, sndrs...)); [20](#exec.when.all-20) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4701) Given subexpressions sndr and env, if[*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") is false, then the expression when_all_with_variant.transform_sender(sndr, env) is ill-formed; otherwise, it is equivalent to:auto&& [_, _, ...child] = sndr;return when_all(into_variant(std::forward_like(child))...); [*Note [1](#exec.when.all-note-1)*: This causes the when_all_with_variant(sndrs...) sender to become when_all(into_variant(sndrs)...) when it is connected with a receiver whose execution domain does not customize when_all_with_variant[.](#exec.when.all-20.sentence-1) — *end note*] #### [33.9.12.13](#exec.into.variant) execution​::​into_variant [[exec.into.variant]](exec.into.variant) [1](#exec.into.variant-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4722) into_variant adapts a sender with multiple value completion signatures into a sender with just one value completion signature consisting of a variant of tuples[.](#exec.into.variant-1.sentence-1) [2](#exec.into.variant-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4727) The name into_variant denotes a pipeable sender adaptor object[.](#exec.into.variant-2.sentence-1) For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.into.variant-2.sentence-2) If Sndr does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),into_variant(sndr) is ill-formed[.](#exec.into.variant-2.sentence-3) [3](#exec.into.variant-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4733) Otherwise, the expression into_variant(sndr) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(into_variant, {}, sndr)) except that sndr is only evaluated once[.](#exec.into.variant-3.sentence-1) [4](#exec.into.variant-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4741) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for into_variant as follows: [🔗](#lib:impls-for%3cinto_variant_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; templatestatic consteval void *check-types*() {auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>(); *decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")}};} [5](#exec.into.variant-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4762) The member *impls-for*​::​*get-state* is initialized with a callable object equivalent to the following lambda:[](Sndr&& sndr, Rcvr& rcvr) noexcept-> type_identity, *FWD-ENV-T*(env_of_t)>> {return {};} [6](#exec.into.variant-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4772) The member *impls-for*​::​*complete* is initialized with a callable object equivalent to the following lambda:[](auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {using variant_type = typename State::type; *TRY-SET-VALUE*(rcvr, variant_type(*decayed-tuple*{std::forward(args)...})); } else { Tag()(std::move(rcvr), std::forward(args)...); }} #### [33.9.12.14](#exec.stopped.opt) execution​::​stopped_as_optional [[exec.stopped.opt]](exec.stopped.opt) [1](#exec.stopped.opt-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4789) stopped_as_optional maps a sender's stopped completion operation into a value completion operation as a disengaged optional[.](#exec.stopped.opt-1.sentence-1) The sender's value completion operation is also converted into an optional[.](#exec.stopped.opt-1.sentence-2) The result is a sender that never completes with stopped, reporting cancellation by completing with a disengaged optional[.](#exec.stopped.opt-1.sentence-3) [2](#exec.stopped.opt-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4797) The name stopped_as_optional denotes a pipeable sender adaptor object[.](#exec.stopped.opt-2.sentence-1) For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.stopped.opt-2.sentence-2) The expression stopped_as_optional(sndr) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(stopped_as_optional, {}, sndr)) except that sndr is only evaluated once[.](#exec.stopped.opt-2.sentence-3) [3](#exec.stopped.opt-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4806) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for stopped_as_optional_t as follows: [🔗](#lib:impls-for%3cstopped_as_optional_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {templatestatic consteval void *check-types*() {*default-impls*::*check-types*(); if constexpr (!requires {requires (![same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]"), *FWD-ENV-T*(Env)...>>); })throw *unspecified-exception*(); }};} where *unspecified-exception* is a type derived from exception[.](#exec.stopped.opt-3.sentence-1) [4](#exec.stopped.opt-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4829) Let sndr and env be subexpressions such that Sndr is decltype((sndr)) andEnv is decltype((env))[.](#exec.stopped.opt-4.sentence-1) If [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") is false then the expression stopped_as_optional.transform_sender(sndr, env) is ill-formed; otherwise, if [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<*child-type*, *FWD-ENV-T*(Env)> is false, the expression stopped_as_optional.transform_sender(sndr, env) is equivalent to *not-a-sender*(); otherwise, it is equivalent to:auto&& [_, _, child] = sndr;using V = *single-sender-value-type*<*child-type*, *FWD-ENV-T*(Env)>;return let_stopped( then(std::forward_like(child), [](Ts&&... ts) noexcept(is_nothrow_constructible_v) {return optional(in_place, std::forward(ts)...); }), []() noexcept { return just(optional()); }); #### [33.9.12.15](#exec.stopped.err) execution​::​stopped_as_error [[exec.stopped.err]](exec.stopped.err) [1](#exec.stopped.err-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4856) stopped_as_error maps an input sender's stopped completion operation into an error completion operation as a custom error type[.](#exec.stopped.err-1.sentence-1) The result is a sender that never completes with stopped, reporting cancellation by completing with an error[.](#exec.stopped.err-1.sentence-2) [2](#exec.stopped.err-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4862) The name stopped_as_error denotes a pipeable sender adaptor object[.](#exec.stopped.err-2.sentence-1) For some subexpressions sndr and err, let Sndr be decltype((sndr)) and let Err be decltype((err))[.](#exec.stopped.err-2.sentence-2) If the type Sndr does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") or if the type Err does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]"),stopped_as_error(sndr, err) is ill-formed[.](#exec.stopped.err-2.sentence-3) Otherwise, the expression stopped_as_error(sndr, err) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(stopped_as_error, err, sndr)) except that sndr is only evaluated once[.](#exec.stopped.err-2.sentence-4) [3](#exec.stopped.err-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4877) Let sndr and env be subexpressions such that Sndr is decltype((sndr)) andEnv is decltype((env))[.](#exec.stopped.err-3.sentence-1) If [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") is false, then the expression stopped_as_error.transform_sender(sndr, env) is ill-formed; otherwise, it is equivalent to:auto&& [_, err, child] = sndr;using E = decltype(auto(err));return let_stopped( std::forward_like(child), [err = std::forward_like(err)]() mutable noexcept(is_nothrow_move_constructible_v) {return just_error(std::move(err)); }); #### [33.9.12.16](#exec.associate) execution​::​associate [[exec.associate]](exec.associate) [1](#exec.associate-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4897) associate tries to associate a sender with an async scope such that the scope can track the lifetime of any asynchronous operations created with the sender[.](#exec.associate-1.sentence-1) [2](#exec.associate-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4903) Let *associate-data* be the following exposition-only class template: [🔗](#lib:execution::associate-data) namespace std::execution {template<[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>struct *associate-data* { // *exposition only*using *wrap-sender* = // *exposition only* remove_cvref_t().wrap(declval()))>; explicit *associate-data*(Token t, Sender&& s): *sndr*(t.wrap(std::forward(s))), *token*(t) {if (!*token*.try_associate())*sndr*.reset(); }*associate-data*(const *associate-data*& other)noexcept(is_nothrow_copy_constructible_v<*wrap-sender*> &&noexcept(other.*token*.try_associate())); *associate-data*(*associate-data*&& other)noexcept(is_nothrow_move_constructible_v<*wrap-sender*>); ~*associate-data*(); optional> release() && noexcept(is_nothrow_move_constructible_v<*wrap-sender*>); private: optional<*wrap-sender*> *sndr*; // *exposition only* Token *token*; // *exposition only*}; template<[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>*associate-data*(Token, Sender&&) -> *associate-data*;} [3](#exec.associate-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4943) For an *associate-data* object a,a.*sndr*.has_value() is true if and only if an association was successfully made and is owned by a[.](#exec.associate-3.sentence-1) [🔗](#lib:execution::associate-data,constructor) `associate-data(const associate-data& other) noexcept(is_nothrow_copy_constructible_v && noexcept(other.token.try_associate())); ` [4](#exec.associate-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4957) *Constraints*: [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]")<*wrap-sender*> is true[.](#exec.associate-4.sentence-1) [5](#exec.associate-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4961) *Effects*: Value-initializes *sndr* and initializes *token* with other.*token*[.](#exec.associate-5.sentence-1) If other.*sndr*.has_value() is false, no further effects; otherwise, calls *token*.try_associate() and, if that returns true, calls *sndr*.emplace(*other.*sndr*) and, if that exits with an exception, calls *token*.disassociate() before propagating the exception[.](#exec.associate-5.sentence-2) [🔗](#lib:execution::associate-data,constructor_) `associate-data(associate-data&& other) noexcept(is_nothrow_move_constructible_v); ` [6](#exec.associate-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4982) *Effects*: Initializes *sndr* with std​::​move(other.*sndr*) and initializes *token* with std​::​move(other.​*token*) and then calls other.*sndr*.reset()[.](#exec.associate-6.sentence-1) [🔗](#lib:execution::associate-data,destructor) `~associate-data(); ` [7](#exec.associate-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4995) *Effects*: If *sndr*.has_value() returns false then no effect; otherwise, invokes *sndr*.reset() before invoking *token*.disassociate()[.](#exec.associate-7.sentence-1) [🔗](#lib:release,execution::associate-data) `optional> release() && noexcept(is_nothrow_move_constructible_v); ` [8](#exec.associate-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5009) *Effects*: If *sndr*.has_value() returns false then returns an optional that does not contain a value; otherwise returns an optional containing a value of type pair as if by:return optional(pair(*token*, std::move(**sndr*))); [9](#exec.associate-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5020) *Postconditions*: *sndr* does not contain a value[.](#exec.associate-9.sentence-1) [10](#exec.associate-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5025) The name associate denotes a pipeable sender adaptor object[.](#exec.associate-10.sentence-1) For subexpressions sndr and token: - [(10.1)](#exec.associate-10.1) If decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), orremove_cvref_t does not satisfy [scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]"), thenassociate(sndr, token) is ill-formed. - [(10.2)](#exec.associate-10.2) Otherwise, the expression associate(sndr, token) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(associate, *associate-data*(token, sndr))) except that sndr is evaluated only once. [11](#exec.associate-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5045) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for associate_t as follows: [🔗](#lib:execution::impls-for%3cassociate_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *get-state* = *see below*; // *exposition only*static constexpr auto *start* = *see below*; // *exposition only*templatestatic consteval void *check-types*() { // *exposition only*using associate_data_t = remove_cvref_t<*data-type*>; using child_type_t = typename associate_data_t::*wrap-sender*; (void)get_completion_signatures(); }};} [12](#exec.associate-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5066) The member *impls-for*​::​*get-state* is initialized with a callable object equivalent to the following lambda: [](Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*) {auto [_, data] = std::forward(sndr); auto dataParts = std::move(data).release(); using scope_tkn = decltype(dataParts->first); using wrap_sender = decltype(dataParts->second); using op_t = connect_result_t; struct op_state {bool *associated* = false; // *exposition only*union { Rcvr* *rcvr*; // *exposition only*struct { scope_tkn *token*; // *exposition only* op_t *op*; // *exposition only*} *assoc*; // *exposition only*}; explicit op_state(Rcvr& r) noexcept: *rcvr*(addressof(r)) {}explicit op_state(scope_tkn tkn, wrap_sender&& sndr, Rcvr& r) try: *associated*(true), *assoc*(tkn, connect(std::move(sndr), std::move(r))) {}catch (...) { tkn.disassociate(); throw; } op_state(op_state&&) = delete; ~op_state() {if (*associated*) {*assoc*.*op*.~op_t(); *assoc*.*token*.disassociate(); *assoc*.*token*.~scope_tkn(); }}void *run*() noexcept { // *exposition only*if (*associated*) start(*assoc*.*op*); else set_stopped(std::move(**rcvr*)); }}; if (dataParts)return op_state{std::move(dataParts->first), std::move(dataParts->second), rcvr}; elsereturn op_state{rcvr};} [13](#exec.associate-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5126) The expression in the noexcept clause of*impls-for*​::​*get-state* isis_nothrow_constructible_v, Sndr> && is_nothrow_move_constructible_v<*wrap-sender*> &&[*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header synopsis [functional.syn]") where *wrap-sender* is the typeremove_cvref_t<*data-type*>​::​*wrap-sender*[.](#exec.associate-13.sentence-1) [14](#exec.associate-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5137) The member *impls-for*​::​*start* is initialized with a callable object equivalent to the following lambda:[](auto& state, auto&) noexcept -> void { state.*run*();} [15](#exec.associate-15) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5146) The evaluation of associate(sndr, token) may cause side effects observable via token's associated async scope object[.](#exec.associate-15.sentence-1) #### [33.9.12.17](#exec.stop.when) Exposition-only execution​::​*stop-when* [[exec.stop.when]](exec.stop.when) [1](#exec.stop.when-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5154) *stop-when* fuses an additional stop token t into a sender so that, upon connecting to a receiver r, the resulting operation state receives stop requests from botht and the token returned from get_stop_token(get_env(r))[.](#exec.stop.when-1.sentence-1) [2](#exec.stop.when-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5161) The name *stop-when* denotes an exposition-only sender adaptor[.](#exec.stop.when-2.sentence-1) For subexpressions sndr and token: - [(2.1)](#exec.stop.when-2.1) If decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), orremove_cvref_t does not satisfy [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]"), then *stop-when*(sndr, token) is ill-formed[.](#exec.stop.when-2.1.sentence-1) - [(2.2)](#exec.stop.when-2.2) Otherwise, if remove_cvref_t models[unstoppable_token](stoptoken.concepts#concept:unstoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]") then*stop-when*(​sndr, token) is expression-equivalent tosndr[.](#exec.stop.when-2.2.sentence-1) - [(2.3)](#exec.stop.when-2.3) Otherwise,*stop-when*(sndr, token) returns a sender osndr[.](#exec.stop.when-2.3.sentence-1) If osndr is connected to a receiver r, let rtoken be the result of get_stop_token(get_env(r))[.](#exec.stop.when-2.3.sentence-2) * [(2.3.1)](#exec.stop.when-2.3.1) If the type of rtoken models [unstoppable_token](stoptoken.concepts#concept:unstoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]") then the effects of connecting osndr to r are equivalent toconnect(write_env(sndr, prop(get_stop_token, token)), r)[.](#exec.stop.when-2.3.1.sentence-1) * [(2.3.2)](#exec.stop.when-2.3.2) Otherwise, the effects of connecting osndr to r are equivalent toconnect(write_env(sndr, prop(get_stop_token, stoken)), r) where stoken is an object of an exposition-only type *stoken-t* such that: + [(2.3.2.1)](#exec.stop.when-2.3.2.1) *stoken-t* models [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]"); + [(2.3.2.2)](#exec.stop.when-2.3.2.2) stoken.stop_requested() returns token.stop_requested() || rtoken.stop_reques- ted(); + [(2.3.2.3)](#exec.stop.when-2.3.2.3) stoken.stop_possible() returns token.stop_possible() || rtoken.stop_possible(); and + [(2.3.2.4)](#exec.stop.when-2.3.2.4) for types Fn and Init such that both [invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]") and [constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_­from [concept.constructible]") are modeled, *stoken-t*​::​callback_type models [*stoppable-callback-for*](stoptoken.concepts#concept:stoppable-callback-for "32.3.3 Stop token concepts [stoptoken.concepts]")[.](#exec.stop.when-2.3.2.sentence-1) [*Note [1](#exec.stop.when-note-1)*: For an object fn of type Fn constructed from a value, init, of type Init, registering fn using *stoken-t*​::​callback_type(stoken, init) results in an invocation of fn when a callback registered with token or rtoken would be invoked[.](#exec.stop.when-2.3.2.4.sentence-2) fn is invoked at most once[.](#exec.stop.when-2.3.2.4.sentence-3) — *end note*] #### [33.9.12.18](#exec.spawn.future) execution​::​spawn_future [[exec.spawn.future]](exec.spawn.future) [1](#exec.spawn.future-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5230) spawn_future attempts to associate the given input sender with the given token's async scope and, on success, eagerly starts the input sender; the return value is a sender that, when connected and started, completes with either the result of the eagerly-started input sender or withset_stopped if the input sender was not started[.](#exec.spawn.future-1.sentence-1) [2](#exec.spawn.future-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5239) The name spawn_future denotes a customization point object[.](#exec.spawn.future-2.sentence-1) For subexpressions sndr, token, and env, - [(2.1)](#exec.spawn.future-2.1) let Sndr be decltype((sndr)), - [(2.2)](#exec.spawn.future-2.2) let Token be remove_cvref_t, and - [(2.3)](#exec.spawn.future-2.3) let Env be remove_cvref_t[.](#exec.spawn.future-2.sentence-2) If any of[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]"), or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") are not satisfied, the expression spawn_future(sndr, token, env) is ill-formed[.](#exec.spawn.future-2.sentence-3) [3](#exec.spawn.future-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5254) Let *spawn-future-state-base* be the exposition-only class template: [🔗](#lib:execution::spawn-future-state-base) namespace std::execution {templatestruct *spawn-future-state-base*; // *exposition only*templatestruct *spawn-future-state-base*> { // *exposition only*using *variant-t* = *see below*; // *exposition only**variant-t* *result*; // *exposition only*virtual void *complete*() noexcept = 0; // *exposition only*};} [4](#exec.spawn.future-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5272) Let Sigs be the pack of arguments to the completion_signatures specialization provided as a parameter to the *spawn-future-state-base* class template[.](#exec.spawn.future-4.sentence-1) Let *as-tuple* be an alias template that transforms a completion signature Tag(Args...) into the tuple specialization *decayed-tuple*[.](#exec.spawn.future-4.sentence-2) - [(4.1)](#exec.spawn.future-4.1) If is_nothrow_constructible_v, Arg> is true for every type Arg in every parameter pack Args in every completion signature Tag(Args...) in Sigs then*variant-t* denotes the typevariant, *as-tuple*...>, except with duplicate types removed[.](#exec.spawn.future-4.1.sentence-1) - [(4.2)](#exec.spawn.future-4.2) Otherwise*variant-t* denotes the typevariant, tuple, *as-tuple*...>, except with duplicate types removed[.](#exec.spawn.future-4.2.sentence-1) [5](#exec.spawn.future-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5298) Let *spawn-future-receiver* be the exposition-only class template: [🔗](#lib:execution::spawn-future-receiver) namespace std::execution {templatestruct *spawn-future-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-future-state-base** *state*; // *exposition only*templatevoid set_value(T&&... t) && noexcept {*set-complete*(std::forward(t)...); }templatevoid set_error(E&& e) && noexcept {*set-complete*(std::forward(e)); }void set_stopped() && noexcept {*set-complete*(); }private:templatevoid *set-complete*(T&&... t) noexcept { // *exposition only*constexpr bool nothrow = (is_nothrow_constructible_v, T> && ...); try {*state*->*result*.template emplace<*decayed-tuple*>(CPO{}, std::forward(t)...); }catch (...) {if constexpr (!nothrow) {using tuple_t = *decayed-tuple*; *state*->*result*.template emplace(set_error_t{}, current_exception()); }}*state*->*complete*(); }};} [6](#exec.spawn.future-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5344) Let *ssource-t* be an unspecified type that models [*stoppable-source*](stoptoken.concepts#concept:stoppable-source "32.3.3 Stop token concepts [stoptoken.concepts]") and let ssource be an lvalue of type *ssource-t*[.](#exec.spawn.future-6.sentence-1) Let *stoken-t* be decltype(ssource.get_token())[.](#exec.spawn.future-6.sentence-2) Let *future-spawned-sender* be the alias template: template<[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender, class Env>using *future-spawned-sender* = // *exposition only*decltype(write_env(*stop-when*(declval(), declval<*stoken-t*>()), declval())); [7](#exec.spawn.future-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5357) Let *spawn-future-state* be the exposition-only class template: [🔗](#lib:execution::spawn-future-state) namespace std::execution {templatestruct *spawn-future-state* // *exposition only*: *spawn-future-state-base*>> {using *sigs-t* = // *exposition only* completion_signatures_of_t<*future-spawned-sender*>; using *receiver-t* = // *exposition only**spawn-future-receiver*<*sigs-t*>; using *op-t* = // *exposition only* connect_result_t<*future-spawned-sender*, *receiver-t*>; *spawn-future-state*(Alloc alloc, Sender&& sndr, Token token, Env env) // *exposition only*: *alloc*(std::move(alloc)), *op*(connect( write_env(*stop-when*(std::forward(sndr), *ssource*.get_token()), std::move(env)), *receiver-t*(this))), *token*(std::move(token)), *associated*(token.try_associate()) {if (associated) start(*op*); else set_stopped(*receiver-t*(this)); }void *complete*() noexcept override; // *exposition only*void *consume*([receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") auto& rcvr) noexcept; // *exposition only*void *abandon*() noexcept; // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits::template rebind_alloc<*spawn-future-state*>; *alloc-t* *alloc*; // *exposition only**ssource-t* *ssource*; // *exposition only**op-t* *op*; // *exposition only* Token *token*; // *exposition only*bool *associated*; // *exposition only*void *destroy*() noexcept; // *exposition only*};} [8](#exec.spawn.future-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5405) For purposes of determining the existence of a data race,*complete*, *consume*, and *abandon* behave as atomic operations ([[intro.multithread]](intro.multithread "6.10.2 Multi-threaded executions and data races"))[.](#exec.spawn.future-8.sentence-1) These operations on a single object of a type that is a specialization of *spawn-future-state* appear to occur in a single total order[.](#exec.spawn.future-8.sentence-2) [🔗](#lib:complete,execution::spawn-future-state) `void complete() noexcept; ` [9](#exec.spawn.future-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5419) *Effects*: - [(9.1)](#exec.spawn.future-9.1) No effects if this invocation of *complete* happens before an invocation of *consume* or *abandon* on *this; - [(9.2)](#exec.spawn.future-9.2) otherwise, if an invocation of *consume* on *this happens before this invocation of *complete* then there is a receiver, rcvr, registered and that receiver is completed as if by *consume*(rcvr); - [(9.3)](#exec.spawn.future-9.3) otherwise,*destroy* is invoked[.](#exec.spawn.future-9.sentence-1) [🔗](#lib:consume,execution::spawn-future-state) `void consume([receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") auto& rcvr) noexcept; ` [10](#exec.spawn.future-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5443) *Effects*: - [(10.1)](#exec.spawn.future-10.1) If this invocation of *consume* happens before an invocation of *complete* on *this thenrcvr is registered to be completed when*complete* is subsequently invoked on *this; - [(10.2)](#exec.spawn.future-10.2) otherwise,rcvr is completed as if by:std::move(this->*result*).visit([&rcvr](auto&& tuple) noexcept {if constexpr (![same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]"), monostate>) { apply([&rcvr](auto cpo, auto&&... vals) { cpo(std::move(rcvr), std::move(vals)...); }, std::move(tuple)); }}); [🔗](#lib:abandon,execution::spawn-future-state) `void abandon() noexcept; ` [11](#exec.spawn.future-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5474) *Effects*: - [(11.1)](#exec.spawn.future-11.1) If this invocation of *abandon* happens before an invocation of *complete* on *this then equivalent to:*ssource*.request_stop(); - [(11.2)](#exec.spawn.future-11.2) otherwise,*destroy* is invoked[.](#exec.spawn.future-11.sentence-1) [🔗](#lib:destroy,execution::spawn-future-state) `void destroy() noexcept; ` [12](#exec.spawn.future-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5496) *Effects*: Equivalent to:auto token = std::move(this->*token*);bool associated = this->*associated*; {auto alloc = std::move(this->*alloc*); allocator_traits<*alloc-t*>::destroy(alloc, this); allocator_traits<*alloc-t*>::deallocate(alloc, this, 1);}if (associated) token.disassociate(); [13](#exec.spawn.future-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5515) The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities")) is specialized for spawn_future_t as follows: [🔗](#lib:execution::impls-for%3cspawn_future_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *start* = *see below*; // *exposition only*};} [14](#exec.spawn.future-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5529) The member *impls-for*​::​*start* is initialized with a callable object equivalent to the following lambda:[](auto& state, auto& rcvr) noexcept -> void { state->*consume*(rcvr);} [15](#exec.spawn.future-15) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5538) For the expression spawn_future(sndr, token, env) let new_sender be the expression token.wrap(sndr) and let alloc and senv be defined as follows: - [(15.1)](#exec.spawn.future-15.1) if the expression get_allocator(env) is well-formed, thenalloc is the result of get_allocator(env) andsenv is the expression env; - [(15.2)](#exec.spawn.future-15.2) otherwise, if the expression get_allocator(get_env(new_sender)) is well-formed, thenalloc is the result of get_allocator(get_env(new_sender)) andsenv is the expression*JOIN-ENV*(prop(get_allocator, alloc), env); - [(15.3)](#exec.spawn.future-15.3) otherwise,alloc is allocator() andsenv is the expression env[.](#exec.spawn.future-15.sentence-1) [16](#exec.spawn.future-16) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5559) The expression spawn_future(sndr, token, env) has the following effects: - [(16.1)](#exec.spawn.future-16.1) Uses alloc to allocate and construct an object s of a type that is a specialization of *spawn-future-​state* from alloc, token.wrap(sndr), token, and senv[.](#exec.spawn.future-16.1.sentence-1) If an exception is thrown then any constructed objects are destroyed and any allocated memory is deallocated[.](#exec.spawn.future-16.1.sentence-2) - [(16.2)](#exec.spawn.future-16.2) Constructs an object u of a type that is a specialization of unique_ptr such that: * [(16.2.1)](#exec.spawn.future-16.2.1) u.get() is equal to the address of s, and * [(16.2.2)](#exec.spawn.future-16.2.2) u.get_deleter()(u.release()) is equivalent to u.release()->*abandon*()[.](#exec.spawn.future-16.2.sentence-1) - [(16.3)](#exec.spawn.future-16.3) Returns *make-sender*(spawn_future, std​::​move(u))[.](#exec.spawn.future-16.3.sentence-1) [17](#exec.spawn.future-17) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5587) The expression spawn_future(sndr, token) is expression-equivalent tospawn_future(sndr, token, execution​::​env<>())[.](#exec.spawn.future-17.sentence-1) ### [33.9.13](#exec.consumers) Sender consumers [[exec.consumers]](exec.consumers) #### [33.9.13.1](#exec.sync.wait) this_thread​::​sync_wait [[exec.sync.wait]](exec.sync.wait) [1](#exec.sync.wait-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5595) this_thread​::​sync_wait and this_thread​::​sync_wait_with_variant are used to block the current thread of execution until the specified sender completes and to return its async result[.](#exec.sync.wait-1.sentence-1) sync_wait mandates that the input sender has exactly one value completion signature[.](#exec.sync.wait-1.sentence-2) [2](#exec.sync.wait-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5604) Let *sync-wait-env* be the following exposition-only class type:namespace std::this_thread {struct *sync-wait-env* { execution::run_loop* *loop*; // *exposition only*auto query(execution::get_scheduler_t) const noexcept {return *loop*->get_scheduler(); }auto query(execution::get_delegation_scheduler_t) const noexcept {return *loop*->get_scheduler(); }};} [3](#exec.sync.wait-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5622) Let *sync-wait-result-type* and*sync-wait-with-variant-result-type* be exposition-only alias templates defined as follows:namespace std::this_thread {template Sndr>using *sync-wait-result-type* = optional>; template Sndr>using *sync-wait-with-variant-result-type* = optional>;} [4](#exec.sync.wait-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5639) The name this_thread​::​sync_wait denotes a customization point object[.](#exec.sync.wait-4.sentence-1) For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.sync.wait-4.sentence-2) The expression this_thread​::​sync_wait(sndr) is expression-equivalent to the following, except that sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait, sndr)*Mandates*: - [(4.1)](#exec.sync.wait-4.1) [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.sync.wait-4.1.sentence-1) - [(4.2)](#exec.sync.wait-4.2) The type *sync-wait-result-type* is well-formed[.](#exec.sync.wait-4.2.sentence-1) - [(4.3)](#exec.sync.wait-4.3) [same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")> is true, where e is the apply_sender expression above[.](#exec.sync.wait-4.3.sentence-1) [5](#exec.sync.wait-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5659) Let *sync-wait-state* and *sync-wait-receiver* be the following exposition-only class templates:namespace std::this_thread {templatestruct *sync-wait-state* { // *exposition only* execution::run_loop *loop*; // *exposition only* exception_ptr *error*; // *exposition only**sync-wait-result-type* *result*; // *exposition only*}; templatestruct *sync-wait-receiver* { // *exposition only*using receiver_concept = execution::receiver_t; *sync-wait-state** *state*; // *exposition only*templatevoid set_value(Args&&... args) && noexcept; templatevoid set_error(Error&& err) && noexcept; void set_stopped() && noexcept; *sync-wait-env* get_env() const noexcept { return {&*state*->*loop*}; }};} [🔗](#exec.sync.wait-itemdecl:1) `template void set_value(Args&&... args) && noexcept; ` [6](#exec.sync.wait-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5695) *Effects*: Equivalent to:try {*state*->*result*.emplace(std::forward(args)...);} catch (...) {*state*->*error* = current_exception();}*state*->*loop*.finish(); [🔗](#exec.sync.wait-itemdecl:2) `template void set_error(Error&& err) && noexcept; ` [7](#exec.sync.wait-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5714) *Effects*: Equivalent to:*state*->*error* = *AS-EXCEPT-PTR*(std::forward(err)); // see [[exec.general]](exec.general "33.1 General")*state*->*loop*.finish(); [🔗](#exec.sync.wait-itemdecl:3) `void set_stopped() && noexcept; ` [8](#exec.sync.wait-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5728) *Effects*: Equivalent to *state*->*loop*.finish()[.](#exec.sync.wait-8.sentence-1) [9](#exec.sync.wait-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5733) For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.sync.wait-9.sentence-1) If [sender_to](#concept:sender_to "33.9.3 Sender concepts [exec.snd.concepts]")> is false, the expression sync_wait.apply_sender(sndr) is ill-formed; otherwise, it is equivalent to:*sync-wait-state* state;auto op = connect(sndr, *sync-wait-receiver*{&state}); start(op); state.*loop*.run();if (state.*error*) { rethrow_exception(std::move(state.*error*));}return std::move(state.*result*); [10](#exec.sync.wait-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5751) The behavior of this_thread​::​sync_wait(sndr) is undefined unless: - [(10.1)](#exec.sync.wait-10.1) It blocks the current thread of execution ([[defns.block]](defns.block "3.6 block")) with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3 Forward progress")) until the specified sender completes[.](#exec.sync.wait-10.1.sentence-1) [*Note [1](#exec.sync.wait-note-1)*: The default implementation of sync_wait achieves forward progress guarantee delegation by providing a run_loop scheduler via the get_delegation_scheduler query on the *sync-wait-receiver*'s environment[.](#exec.sync.wait-10.1.sentence-2) The run_loop is driven by the current thread of execution[.](#exec.sync.wait-10.1.sentence-3) — *end note*] - [(10.2)](#exec.sync.wait-10.2) It returns the specified sender's async results as follows: * [(10.2.1)](#exec.sync.wait-10.2.1) For a value completion, the result datums are returned in a tuple in an engaged optional object[.](#exec.sync.wait-10.2.1.sentence-1) * [(10.2.2)](#exec.sync.wait-10.2.2) For an error completion, an exception is thrown[.](#exec.sync.wait-10.2.2.sentence-1) * [(10.2.3)](#exec.sync.wait-10.2.3) For a stopped completion, a disengaged optional object is returned[.](#exec.sync.wait-10.2.3.sentence-1) #### [33.9.13.2](#exec.sync.wait.var) this_thread​::​sync_wait_with_variant [[exec.sync.wait.var]](exec.sync.wait.var) [1](#exec.sync.wait.var-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5781) The name this_thread​::​sync_wait_with_variant denotes a customization point object[.](#exec.sync.wait.var-1.sentence-1) For a subexpression sndr, let Sndr be decltype(into_variant(sndr))[.](#exec.sync.wait.var-1.sentence-2) The expression this_thread​::​sync_wait_with_variant(sndr) is expression-equivalent to the following, except sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait_with_variant, sndr)*Mandates*: - [(1.1)](#exec.sync.wait.var-1.1) [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.sync.wait.var-1.1.sentence-1) - [(1.2)](#exec.sync.wait.var-1.2) The type *sync-wait-with-variant-result-type* is well-formed[.](#exec.sync.wait.var-1.2.sentence-1) - [(1.3)](#exec.sync.wait.var-1.3) [same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")> is true, where e is the apply_sender expression above[.](#exec.sync.wait.var-1.3.sentence-1) [2](#exec.sync.wait.var-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5804) The expression sync_wait_with_variant.apply_sender(sndr) is equivalent to:using result_type = *sync-wait-with-variant-result-type*;if (auto opt_value = sync_wait(into_variant(sndr))) {return result_type(std::move(get<0>(*opt_value)));}return result_type(nullopt); [3](#exec.sync.wait.var-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5814) The behavior of this_thread​::​sync_wait_with_variant(sndr) is undefined unless: - [(3.1)](#exec.sync.wait.var-3.1) It blocks the current thread of execution ([[defns.block]](defns.block "3.6 block")) with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3 Forward progress")) until the specified sender completes[.](#exec.sync.wait.var-3.1.sentence-1) [*Note [1](#exec.sync.wait.var-note-1)*: The default implementation of sync_wait_with_variant achieves forward progress guarantee delegation by relying on the forward progress guarantee delegation provided by sync_wait[.](#exec.sync.wait.var-3.1.sentence-2) — *end note*] - [(3.2)](#exec.sync.wait.var-3.2) It returns the specified sender's async results as follows: * [(3.2.1)](#exec.sync.wait.var-3.2.1) For a value completion, the result datums are returned in an engaged optional object that contains a variant of tuples[.](#exec.sync.wait.var-3.2.1.sentence-1) * [(3.2.2)](#exec.sync.wait.var-3.2.2) For an error completion, an exception is thrown[.](#exec.sync.wait.var-3.2.2.sentence-1) * [(3.2.3)](#exec.sync.wait.var-3.2.3) For a stopped completion, a disengaged optional object is returned[.](#exec.sync.wait.var-3.2.3.sentence-1) #### [33.9.13.3](#exec.spawn) execution​::​spawn [[exec.spawn]](exec.spawn) [1](#exec.spawn-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5843) spawn attempts to associate the given input sender with the given token's async scope and, on success, eagerly starts the input sender[.](#exec.spawn-1.sentence-1) [2](#exec.spawn-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5848) The name spawn denotes a customization point object[.](#exec.spawn-2.sentence-1) For subexpressions sndr, token, and env, - [(2.1)](#exec.spawn-2.1) let Sndr be decltype((sndr)), - [(2.2)](#exec.spawn-2.2) let Token be remove_cvref_t, and - [(2.3)](#exec.spawn-2.3) let Env be remove_cvref_t[.](#exec.spawn-2.sentence-2) If any of[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]"), or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") are not satisfied, the expression spawn(​sndr, token, env) is ill-formed[.](#exec.spawn-2.sentence-3) [3](#exec.spawn-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5863) Let *spawn-state-base* be the exposition-only class: [🔗](#lib:execution::spawn-state-base) namespace std::execution {struct *spawn-state-base* { // *exposition only*virtual void *complete*() noexcept = 0; // *exposition only*};} [4](#exec.spawn-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5875) Let *spawn-receiver* be the exposition-only class: [🔗](#lib:execution::spawn-receiver) namespace std::execution {struct *spawn-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-state-base** *state*; // *exposition only*void set_value() && noexcept { *state*->*complete*(); }void set_stopped() && noexcept { *state*->*complete*(); }};} [5](#exec.spawn-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5891) Let *spawn-state* be the exposition-only class template: [🔗](#lib:execution::spawn-state) namespace std::execution {templatestruct *spawn-state* : *spawn-state-base* { // *exposition only*using *op-t* = connect_result_t; // *exposition only**spawn-state*(Alloc alloc, Sender&& sndr, Token token); // *exposition only*void *complete*() noexcept override; // *exposition only*void *run*(); // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits::template rebind_alloc<*spawn-state*>; *alloc-t* *alloc*; // *exposition only**op-t* *op*; // *exposition only* Token *token*; // *exposition only*void *destroy*() noexcept; // *exposition only*};} [🔗](#lib:execution::spawn-state,constructor) `spawn-state(Alloc alloc, Sender&& sndr, Token token); ` [6](#exec.spawn-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5924) *Effects*: Initializes*alloc* with alloc,*token* with token, and*op* with:connect(std::move(sndr), *spawn-receiver*(this)) [🔗](#lib:run,execution::spawn-state) `void run(); ` [7](#exec.spawn-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5941) *Effects*: Equivalent to:if (*token*.try_associate()) start(*op*);else*destroy*(); [🔗](#lib:complete,execution::spawn-state) `void complete() noexcept override; ` [8](#exec.spawn-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5958) *Effects*: Equivalent to:auto token = std::move(this->*token*); *destroy*(); token.disassociate(); [🔗](#lib:destroy,execution::spawn-state) `void destroy() noexcept; ` [9](#exec.spawn-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5975) *Effects*: Equivalent to:auto alloc = std::move(this->*alloc*); allocator_traits<*alloc-t*>::destroy(alloc, this); allocator_traits<*alloc-t*>::deallocate(alloc, this, 1); [10](#exec.spawn-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5986) For the expression spawn(sndr, token, env) let new_sender be the expression token.wrap(sndr) and let alloc and senv be defined as follows: - [(10.1)](#exec.spawn-10.1) if the expression get_allocator(env) is well-formed, thenalloc is the result of get_allocator(env) andsenv is the expression env, - [(10.2)](#exec.spawn-10.2) otherwise if the expression get_allocator(get_env(new_sender)) is well-formed, thenalloc is the result of get_allocator(get_env(new_sender)) andsenv is the expression *JOIN-ENV*(prop(get_allocator, alloc), env), - [(10.3)](#exec.spawn-10.3) otherwisealloc is allocator() andsenv is the expression env[.](#exec.spawn-10.sentence-1) [11](#exec.spawn-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6006) The expression spawn(sndr, token, env) is of type void and has the following effects: - [(11.1)](#exec.spawn-11.1) Uses alloc to allocate and construct an object o of type that is a specialization of *spawn-state* fromalloc, write_env(token.wrap(sndr), senv), and token and then invokes o.*run*()[.](#exec.spawn-11.sentence-1) If an exception is thrown then any constructed objects are destroyed and any allocated memory is deallocated[.](#exec.spawn-11.1.sentence-2) [12](#exec.spawn-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6021) The expression spawn(sndr, token) is expression-equivalent tospawn(sndr, token, execution​::​env<>(​))[.](#exec.spawn-12.sentence-1)