Files
cppdraft_translate/cppdraft/exec/snd.md
2025-10-25 03:02:53 +03:00

3838 lines
198 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[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.11Sender factories") and [[exec.adapt]](#exec.adapt "33.9.12Sender 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.2Equality 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.3Sender concepts[exec.snd.concepts]")<Sndr, Rcvr> 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.3Asynchronous operations"))
necessarily results in the potential evaluation ([[basic.def.odr]](basic.def.odr "6.3One-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<Sndr, Env>()[.](#general-1.sentence-6)
Then CS is
a specialization of
the class template completion_signatures ([[exec.cmplsig]](exec.cmplsig "33.10Completion 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<Sndr>() 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<Sndr, Env> 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<Sndr> andcompletion_signatures_of_t<Sndr, Env> 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.2queryable 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<Env>()))[.](#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.2queryable concept[exec.queryable.concept]") such that the result of env.query(q) has
a value equal to v ([[concepts.equality]](concepts.equality "18.2Equality 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.2queryable 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.2queryable concept[exec.queryable.concept]") such that o1.query(get_completion_scheduler<Tag>) 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.2queryable 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<class Default = default_domain, class Sndr>
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<T>(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<Default>(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.7Other transformations"))
(ignoring those types that are ill-formed),
then *completion-domain*<Default>(sndr) is
a default-constructed prvalue of that type[.](#expos-9.sentence-2)
Otherwise, *completion-domain*<Default>(sndr) is ill-formed[.](#expos-9.sentence-3)
[🔗](#expos-itemdecl:2)
`template<class Tag, class Env, class Default>
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<Default>(std::forward<Default>(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<class Sndr>
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<class Sndr, class Env>
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.3Sender concepts[exec.snd.concepts]")<Sndr, continues_on_t> 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.7execution::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*<void>(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.2Header <functional> synopsis[functional.syn]") Fun>requires is_nothrow_move_constructible_v<Fun>struct *emplace-from* { Fun *fun*; // *exposition only*using type = *call-result-t*<Fun>; constexpr operator type() && noexcept([*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2Header <functional> synopsis[functional.syn]")<Fun>) {return std::move(fun)(); }constexpr type operator()() && noexcept([*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2Header <functional> synopsis[functional.syn]")<Fun>) {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)
template<class T0, class T1, …, class Tn>struct *product-type* { // *exposition only* T0 t0; // *exposition only* T1 t1; // *exposition only*
Tn tn; // *exposition only*template<size_t I, class Self>constexpr decltype(auto) *get*(this Self&& self) noexcept; // *exposition only*template<class Self, class Fn>constexpr 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.7Structured binding declarations"))[.](#expos-18.sentence-3)
— *end note*]
[🔗](#expos-itemdecl:5)
`template<size_t I, class Self>
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<Self>(ts...[I]);
template<class Self, class Fn>constexpr 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>(fn)(std::forward_like<Self>(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.2Exposition-only entities[exec.snd.expos]") be the following concept:namespace std::execution {template<template<class...> class T, class... Args>concept [*valid-specialization*](#concept:valid-specialization "33.9.2Exposition-only entities[exec.snd.expos]") = // *exposition only*requires { typename T<Args...>; };}
[🔗](#expos-itemdecl:6)
`template<class Tag, class Data = see below, class... Child>
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.6Object concepts[concepts.object]")<Tag>
- [(24.2)](#expos-24.2)
[*movable-value*](exec.general#concept:movable-value "33.1General[exec.general]")<Data>
- [(24.3)](#expos-24.3)
([sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Child> && ...)
- [(24.4)](#expos-24.4)
[dependent_sender](#concept:dependent_sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> || [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr>,
where Sndr is *basic-sender*<Tag, Data,
Child...> as defined below[.](#expos-24.sentence-1)
*Recommended practice*: If evaluation of [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> results in
an uncaught exception from
the evaluation of get_completion_signatures<Sndr>(),
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*<Tag, decay_t<Data>, decay_t<Child>...> 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.6Object concepts[concepts.object]")[.](#expos-26.sentence-1)
namespace std::execution {template<class Tag>concept [*completion-tag*](#concept:completion-tag "33.9.2Exposition-only entities[exec.snd.expos]") = // *exposition only*[same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, set_value_t> || [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, set_error_t> || [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, set_stopped_t>; 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*template<class Sndr, class... Env>static consteval void *check-types*(); // *exposition only*}; template<class Tag>struct *impls-for* : *default-impls* {}; // *exposition only*template<class Sndr, class Rcvr> // *exposition only*using *state-type* = decay_t<*call-result-t*<decltype(*impls-for*<tag_of_t<Sndr>>::*get-state*), Sndr, Rcvr&>>; template<class Index, class Sndr, class Rcvr> // *exposition only*using *env-type* = *call-result-t*<decltype(*impls-for*<tag_of_t<Sndr>>::*get-env*), Index, *state-type*<Sndr, Rcvr>&, const Rcvr&>; template<class Sndr>using *data-type* = decltype(declval<Sndr>().template *get*<1>()); // *exposition only*template<class Sndr, size_t I = 0>using *child-type* = decltype(declval<Sndr>().template *get*<I+2>()); // *exposition only*template<class Sndr>using *indices-for* = remove_reference_t<Sndr>::*indices-for*; // *exposition only*template<class Sndr, class Rcvr>struct *basic-state* { // *exposition only**basic-state*(Sndr&& sndr, Rcvr&& rcvr) noexcept(*see below*): *rcvr*(std::move(rcvr)) , *state*(*impls-for*<tag_of_t<Sndr>>::*get-state*(std::forward<Sndr>(sndr), *rcvr*)) { } Rcvr *rcvr*; // *exposition only**state-type*<Sndr, Rcvr> *state*; // *exposition only*}; template<class Sndr, class Rcvr, class Index>requires [*valid-specialization*](#concept:valid-specialization "33.9.2Exposition-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<Sndr>; // *exposition only*using *state-t* = *state-type*<Sndr, Rcvr>; // *exposition only*static constexpr const auto& *complete* = *impls-for*<*tag-t*>::*complete*; // *exposition only*template<class... Args>requires [*callable*](functional.syn#concept:callable "22.10.2Header <functional> synopsis[functional.syn]")<decltype(*complete*), Index, *state-t*&, Rcvr&, set_value_t, Args...>void set_value(Args&&... args) && noexcept {*complete*(Index(), op->*state*, op->*rcvr*, set_value_t(), std::forward<Args>(args)...); }template<class Error>requires [*callable*](functional.syn#concept:callable "22.10.2Header <functional> synopsis[functional.syn]")<decltype(*complete*), Index, *state-t*&, Rcvr&, set_error_t, Error>void set_error(Error&& err) && noexcept {*complete*(Index(), op->*state*, op->*rcvr*, set_error_t(), std::forward<Error>(err)); }void set_stopped() && noexceptrequires [*callable*](functional.syn#concept:callable "22.10.2Header <functional> synopsis[functional.syn]")<decltype(*complete*), Index, *state-t*&, Rcvr&, set_stopped_t> {*complete*(Index(), op->*state*, op->*rcvr*, set_stopped_t()); }auto get_env() const noexcept -> *env-type*<Index, Sndr, Rcvr> {return *impls-for*<tag-t>::*get-env*(Index(), op->*state*, op->*rcvr*); }*basic-state*<Sndr, Rcvr>* *op*; // *exposition only*}; constexpr auto *connect-all* = *see below*; // *exposition only*template<class Sndr, class Rcvr>using *connect-all-result* = *call-result-t*< // *exposition only*decltype(*connect-all*), *basic-state*<Sndr, Rcvr>*, Sndr, *indices-for*<Sndr>>; template<class Sndr, class Rcvr>requires [*valid-specialization*](#concept:valid-specialization "33.9.2Exposition-only entities[exec.snd.expos]")<*state-type*, Sndr, Rcvr> &&[*valid-specialization*](#concept:valid-specialization "33.9.2Exposition-only entities[exec.snd.expos]")<*connect-all-result*, Sndr, Rcvr>struct *basic-operation* : *basic-state*<Sndr, Rcvr> { // *exposition only*using operation_state_concept = operation_state_t; using *tag-t* = tag_of_t<Sndr>; // *exposition only**connect-all-result*<Sndr, Rcvr> *inner-ops*; // *exposition only**basic-operation*(Sndr&& sndr, Rcvr&& rcvr) noexcept(*see below*) // *exposition only*: *basic-state*<Sndr, Rcvr>(std::forward<Sndr>(sndr), std::move(rcvr)), *inner-ops*(*connect-all*(this, std::forward<Sndr>(sndr), *indices-for*<Sndr>())){}void start() & noexcept {auto& [...ops] = *inner-ops*; *impls-for*<tag-t>::*start*(this->*state*, this->*rcvr*, ops...); }}; template<class Tag, class Data, class... Child>struct *basic-sender* : *product-type*<Tag, Data, Child...> { // *exposition only*using sender_concept = sender_t; using *indices-for* = index_sequence_for<Child...>; // *exposition only*decltype(auto) get_env() const noexcept {auto& [_, data, ...child] = *this; return *impls-for*<Tag>::*get-attrs*(data, child...); }template<[*decays-to*](execution.syn#concept:decays-to "33.4Header <execution> synopsis[execution.syn]")<*basic-sender*> Self, [receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>auto connect(this Self&& self, Rcvr rcvr) noexcept(*see below*)-> *basic-operation*<Self, Rcvr> {return {std::forward<Self>(self), std::move(rcvr)}; }template<[*decays-to*](execution.syn#concept:decays-to "33.4Header <execution> 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.7Structured 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<Rcvr> &&[*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2Header <functional> synopsis[functional.syn]")<decltype(*impls-for*<tag_of_t<Sndr>>::*get-state*), Sndr, Rcvr&> &&([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<*state-type*<Sndr, Rcvr>, *get-state-result*> || is_nothrow_constructible_v<*state-type*<Sndr, Rcvr>, *get-state-result*>) where *get-state-result* is*call-result-t*<decltype(*impls-for*<tag_of_t<Sndr>>::*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)
`[]<class Sndr, class Rcvr, size_t... Is>(
basic-state<Sndr, Rcvr>* op, Sndr&& sndr, index_sequence<Is...>) noexcept(see below)
-> decltype(auto) {
auto& [_, data, ...child] = sndr;
return product-type{connect(
std::forward_like<Sndr>(child),
basic-receiver<Sndr, Rcvr, integral_constant<size_t, Is>>{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>, Self, Rcvr> &&noexcept(*connect-all*(this, std::forward<Sndr>(sndr), *indices-for*<Sndr>()))
[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>, 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:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) {auto& [_, data, ...child] = sndr; return *allocator-aware-forward*(std::forward_like<Sndr>(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:[]<class Index, class Rcvr, class Tag, class... Args>( Index, auto& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires [*callable*](functional.syn#concept:callable "22.10.2Header <functional> synopsis[functional.syn]")<Tag, Rcvr, Args...> {static_assert(Index::value == 0);
Tag()(std::move(rcvr), std::forward<Args>(args)...);}
[🔗](#lib:check-types,default-impls)
`template<class Sndr, class... Env>
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*<Sndr>[.](#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*<Sndr, Is>, *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*<T>::*check-types*<S, E...>()[.](#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<class Tag, class Data, class... Child>
template<class Sndr, class... Env>
constexpr auto basic-sender<Tag, Data, Child...>::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*<Tag>::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<Sndr, Rcvr>[.](#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.3One-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)
template<class... Fns>struct *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; template<class Sndr>static 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*([]<class Tag, class... Ts>(Tag(*)(Ts...)) {if constexpr (!(is_constructible_v<decay_t<Ts>, Ts> &&...))throw *unspecified-exception*(); });} where *unspecified-exception* is
a type derived from exception[.](#expos-48.sentence-1)
[🔗](#expos-itemdecl:10)
`template<class T, class Context>
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<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<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<T>(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<decltype(e)>(std::forward_like<T>(e), *alloc*) where e is the corresponding element of obj,
- [(50.3)](#expos-50.3)
otherwise, returns make_obj_using_allocator<P>(std::forward<T>(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.3Sender concepts[exec.snd.concepts]") concept defines
the requirements for a sender type ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#concepts-1.sentence-1)
The [sender_in](#concept:sender_in "33.9.3Sender 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.3Sender 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.3Asynchronous operations"))
a sender and a receiver to produce an operation state[.](#concepts-1.sentence-5)
[🔗](#lib:is-dependent-sender-helper)
namespace std::execution {template<auto>concept [*is-constant*](#concept:is-constant "33.9.3Sender concepts[exec.snd.concepts]") = true; // *exposition only*template<class Sndr>concept [*is-sender*](#concept:is-sender "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<typename Sndr::sender_concept, sender_t>; template<class Sndr>concept [*enable-sender*](#concept:enable-sender "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[*is-sender*](#concept:is-sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> ||[*is-awaitable*](#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Sndr, *env-promise*<env<>>>; // [[exec.awaitable]](#exec.awaitable "33.9.4Awaitable helpers")template<class Sndr>inline constexpr bool enable_sender = [*enable-sender*](#concept:enable-sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr>; template<class Sndr>consteval bool *is-dependent-sender-helper*() try { // *exposition only* get_completion_signatures<Sndr>(); return false; } catch (dependent_sender_error&) {return true; }template<class Sndr>concept [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") = enable_sender<remove_cvref_t<Sndr>> &&requires (const remove_cvref_t<Sndr>& sndr) {{ get_env(sndr) } -> [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]"); } &&[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13Concept move_­constructible[concept.moveconstructible]")<remove_cvref_t<Sndr>> &&[constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<remove_cvref_t<Sndr>, Sndr>; template<class Sndr, class... Env>concept [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]") =[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> &&(sizeof...(Env) <= 1) &&([*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")<Env> &&...) &&[*is-constant*](#concept:is-constant "33.9.3Sender concepts[exec.snd.concepts]")<get_completion_signatures<Sndr, Env...>()>; template<class Sndr>concept [dependent_sender](#concept:dependent_sender "33.9.3Sender concepts[exec.snd.concepts]") =[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> && bool_constant<*is-dependent-sender-helper*<Sndr>()>::value; template<class Sndr, class Rcvr>concept [sender_to](#concept:sender_to "33.9.3Sender concepts[exec.snd.concepts]") =[sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, env_of_t<Rcvr>> &&[receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>> &&requires (Sndr&& sndr, Rcvr&& rcvr) { connect(std::forward<Sndr>(sndr), std::forward<Rcvr>(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.3Sender concepts[exec.snd.concepts]")<Sndr> is true and[dependent_sender](#concept:dependent_sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> is false,
then Sndr is a non-dependent sender ([[exec.async.ops]](exec.async.ops "33.3Asynchronous 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.3Sender 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<Sndr, Env>[.](#concepts-3.sentence-2)
Sndr and Env model [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, Env> 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.1Namespace std"),
users may specialize enable_sender totrue for cv-unqualified program-defined types that
model [sender](#concept:sender "33.9.3Sender 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.7Constant 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.3Sender concepts[exec.snd.concepts]") and [*sender-in-of*](#concept:sender-in-of "33.9.3Sender 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 {template<class... As>using *value-signature* = set_value_t(As...); // *exposition only*template<class Sndr, class SetValue, class... Env>concept [*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, Env...> &&*MATCHING-SIG*(SetValue, // see [[exec.general]](exec.general "33.1General")*gather-signatures*<set_value_t, // see [[exec.cmplsig]](exec.cmplsig "33.10Completion signatures") completion_signatures_of_t<Sndr, Env...>, *value-signature*,
type_identity_t>); template<class Sndr, class Env, class... Values>concept [*sender-in-of*](#concept:sender-in-of "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, set_value_t(Values...), Env>; template<class Sndr, class... Values>concept [*sender-of*](#concept:sender-of "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, set_value_t(Values...)>;}
[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<Sndr> is as follows:
- [(6.1)](#concepts-6.1)
If the declarationauto&& [tag, data, ...children] = sndr; would be well-formed, tag_of_t<Sndr> is
an alias for decltype(auto(tag)).
- [(6.2)](#concepts-6.2)
Otherwise, tag_of_t<Sndr> 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.3Sender concepts[exec.snd.concepts]") be an exposition-only concept defined as follows:namespace std::execution {template<class Sndr, class Tag>concept [*sender-for*](#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]") =[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> &&[same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<tag_of_t<Sndr>, 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.14Concept 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 "33Execution control library"), an [*awaitable*](#def:awaitable "33.9.4Awaitable 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.4Await[expr.await]") in a coroutine,
resulting in lvalue e as described by [[expr.await]](expr.await "7.6.2.4Await"),
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<void>[.](#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.4Awaitable helpers[exec.awaitable]") be the following exposition-only concept:namespace std {template<class T>concept [*await-suspend-result*](#concept:await-suspend-result "33.9.4Awaitable helpers[exec.awaitable]") = *see below*; // *exposition only*template<class A, class... Promise>concept [*is-awaiter*](#concept:is-awaiter "33.9.4Awaitable helpers[exec.awaitable]") = // *exposition only*requires (A& a, coroutine_handle<Promise...> h) { a.await_ready() ? 1 : 0; { a.await_suspend(h) } -> [*await-suspend-result*](#concept:await-suspend-result "33.9.4Awaitable helpers[exec.awaitable]");
a.await_resume(); }; template<class C, class... Promise>concept [*is-awaitable*](#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]") = // *exposition only*requires (C (*fc)() noexcept, Promise&... p) {{ *GET-AWAITER*(fc(), p...) } -> [*is-awaiter*](#concept:is-awaiter "33.9.4Awaitable helpers[exec.awaitable]")<Promise...>; };}
[*await-suspend-result*](#concept:await-suspend-result "33.9.4Awaitable helpers[exec.awaitable]")<T> 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*<C, Promise> denotes
the type decltype(*GET-AWAITER*(c, p).await_resume()) and*await-result-type*<C> 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 {template<class T, class Promise>concept [*has-as-awaitable*](#concept:has-as-awaitable "33.9.4Awaitable helpers[exec.awaitable]") = // *exposition only*requires (T&& t, Promise& p) {{ std::forward<T>(t).as_awaitable(p) } -> [*is-awaitable*](#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Promise&>; }; template<class Derived>struct *with-await-transform* { // *exposition only*template<class T> T&& await_transform(T&& value) noexcept {return std::forward<T>(value); }template<[*has-as-awaitable*](#concept:has-as-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Derived> T>auto await_transform(T&& value)noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>())))-> decltype(std::forward<T>(value).as_awaitable(declval<Derived&>())) {return std::forward<T>(value).as_awaitable(static_cast<Derived&>(*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 {template<class Env>struct *env-promise* : *with-await-transform*<*env-promise*<Env>> { // *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.5execution::default_­domain[exec.domain.default]") {template<[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")... Env>requires (sizeof...(Env) <= 1)static constexpr [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)noexcept(*see below*); template<[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") Env>static constexpr [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept; template<class Tag, [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, class... Args>static constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)noexcept(*see below*); };}
[🔗](#lib:transform_sender,default_domain)
`template<[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")... Env>
requires (sizeof...(Env) <= 1)
constexpr [sender](#concept:sender "33.9.3Sender 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<Sndr>().transform_sender(std::forward<Sndr>(sndr), env...) if that expression is well-formed;
otherwise, std::forward<Sndr>(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.3Sender concepts[exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") Env>
constexpr [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable 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<Sndr>().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env)) if that expression is well-formed;
otherwise, *FWD-ENV*(std::forward<Env>(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<class Tag, [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, class... Args>
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>(sndr), std::forward<Args>(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<class Domain, [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")... Env>
requires (sizeof...(Env) <= 1)
constexpr [sender](#concept:sender "33.9.3Sender 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>(sndr), env...) if that expression is well-formed; otherwise,default_domain().transform_sender(std::forward<Sndr>(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<class Domain, [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") Env>
constexpr [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable 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>(sndr), std::forward<Env>(env)) if that expression is well-formed; otherwise,default_domain().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(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<class Domain, class Tag, [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, class... Args>
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>(sndr), std::forward<Args>(args)...) if that expression is well-formed; otherwise,default_domain().apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(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<class Sndr, class... Env>
consteval auto get_completion_signatures() -> [valid-completion-signatures](execution.syn#concept:valid-completion-signatures "33.4Header <execution> 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.13Concept move_­constructible[concept.moveconstructible]")<Except> && [derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<Except, exception> 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.4Header <execution> synopsis[execution.syn]");
otherwise, it is the following expression:(e, throw except, completion_signatures())
Let *get-complsigs*<Sndr, Env...>() be expression-equivalent toremove_reference_t<Sndr>::template get_completion_signatures<Sndr, Env...>()[.](#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<Sndr>(), declval<Env>()...),
declval<Sndr>(),
declval<Env>()...)
[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*<NewSndr, Env...>()) if *get-complsigs*<NewSndr, Env
...>() is a well-formed expression[.](#exec.getcomplsigs-3.1.sentence-1)
- [(3.2)](#exec.getcomplsigs-3.2)
Otherwise,*CHECKED-COMPLSIGS*(*get-complsigs*<NewSndr>()) if *get-complsigs*<NewSndr>() 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*<NewSndr, *env-promise*<Env>...>), // [[exec.snd.concepts]](#concepts "33.9.3Sender concepts") set_error_t(exception_ptr),
set_stopped_t()> if [*is-awaitable*](#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<NewSndr, *env-promise*<Env>...> 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<Sndr> andcompletion_signatures_of_t<Sndr, Env> 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.1Receiver concepts[exec.recv.concepts]"), and
let Sndr be the type of a sender
such that [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, env_of_t<Rcvr>> is true[.](#exec.getcomplsigs-5.sentence-1)
Let Sigs... be the template arguments of
the completion_signatures specialization
named by completion_signatures_of_t<Sndr, env_of_t<Rcvr>>[.](#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.3One-definition rule"))
then there shall be a signature Sig in Sigs... such that*MATCHING-SIG*(*decayed-typeof*<CSO>(decltype(args)...), Sig) is true ([[exec.general]](exec.general "33.1General"))[.](#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.3Asynchronous 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<decltype((new_sndr))> and decay_t<Rcvr>, 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<DR> 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*<DS, *connect-awaitable-promise*>,
let Sigs name the typecompletion_signatures<*SET-VALUE-SIG*(V), // see [[exec.snd.concepts]](#concepts "33.9.3Sender concepts") set_error_t(exception_ptr),
set_stopped_t()> and let *connect-awaitable* be an exposition-only coroutine
defined as follows:namespace std::execution {template<class Fun, class... Ts>auto *suspend-complete*(Fun fun, Ts&&... as) noexcept { // *exposition only*auto fn = [&, fun]() noexcept { fun(std::forward<Ts>(as)...); }; struct awaiter {decltype(fn) *fn*; // *exposition only*static constexpr bool await_ready() noexcept { return false; }void await_suspend(coroutine_handle<>) noexcept { *fn*(); }[[noreturn]] void await_resume() noexcept { unreachable(); }}; return awaiter{fn}; }*operation-state-task* *connect-awaitable*(DS sndr, DR rcvr) requires [receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<DR, Sigs> { exception_ptr ep; try {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<V, void>) {co_await std::move(sndr); co_await *suspend-complete*(set_value, std::move(rcvr)); } else {co_await *suspend-complete*(set_value, std::move(rcvr), co_await std::move(sndr)); }} catch(...) { ep = current_exception(); }co_await *suspend-complete*(set_error, std::move(rcvr), std::move(ep)); }}
[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.1General[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.3Sender concepts[exec.snd.concepts]")<Sndr, env_of_t<Rcvr>>
- [(6.4)](#exec.connect-6.4)
[receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>>
### [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.3Asynchronous 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.3Sender 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<set_value_t>(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.1General[exec.general]")<Ts> &&...) 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.2Exposition-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.2Exposition-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*<read_env>> : *default-impls* {static constexpr auto start =[](auto query, auto& rcvr) noexcept -> void {*TRY-SET-VALUE*(rcvr, query(get_env(rcvr))); }; }; template<class Sndr, class Env>static consteval void *check-types*();}
[🔗](#lib:check-types,impls-for%3cdecayed-typeof%3cread_env%3e%3e)
`template<class Sndr, class Env>
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*<Sndr>>[.](#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.12Sender 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.3Asynchronous 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.1forwarding_­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.3Asynchronous 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.12Sender adaptors") is specified to include set_error_t(Err) among its set of completion signatures
where decay_t<Err> 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.2Closure objects[exec.adapt.obj]") is a function object
that accepts one or more [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") arguments and returns a [sender](#concept:sender "33.9.3Sender 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.3Sender concepts[exec.snd.concepts]"),
the following expressions are equivalent and yield a [sender](#concept:sender "33.9.3Sender 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.4Requirements"))
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.3Definitions")) 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.3Concept derived_­from[concept.derived]")<sender_adaptor_closure<T>>,T has no other base classes
of type sender_adaptor_closure<U> for any other type U, andT does not satisfy [sender](#concept:sender "33.9.3Sender 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.3Concept derived_­from[concept.derived]")<sender_adaptor_closure<D>>[.](#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.2Closure objects[exec.adapt.obj]") is a customization point object
that accepts a [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") as its first argument and
returns a [sender](#concept:sender "33.9.3Sender 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.3Sender 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<decltype((args))>(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.2queryable 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.3Sender concepts[exec.snd.concepts]") or
if decltype((env)) does not satisfy [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable 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.2Exposition-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))); }; template<class Sndr, class... Env>static 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.2queryable 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*<Sndr> and
let JoinEnv be the packdecltype(*join-env*(declval<State>(), *FWD-ENV*(declval<Env>())))[.](#exec.write.env-5.sentence-1)
Then *impls-for*<*write-env-t*>::*check-types*<Sndr, Env...>() is expression-equivalent toget_completion_signatures<*child-
type*<Sndr>, 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.6Schedulers[exec.sched]"), ordecltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3Sender 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.3Sender concepts[exec.snd.concepts]")<OutSndr, starts_on_t> 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<OutSndr>(sndr)]() mutablenoexcept(is_nothrow_move_constructible_v<decay_t<OutSndr>>) {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.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> 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.6Schedulers[exec.sched]"), ordecltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3Sender 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.2Exposition-only entities"))
is specialized for continues_on_t as follows:namespace std::execution {template<>struct *impls-for*<continues_on_t> : *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.3Sender concepts[exec.snd.concepts]")<Sndr, continues_on_t> 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.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> 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.6Schedulers[exec.sched]"), orSndr does not satisfy [sender](#concept:sender "33.9.3Sender 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.2Exposition-only entities"))
is specialized for schedule_from_t as follows:
[🔗](#lib:impls-for%3cschedule_from_t%3e)
namespace std::execution {template<>struct *impls-for*<schedule_from_t> : *default-impls* {static constexpr auto *get-attrs* = *see below*; static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
[5](#exec.schedule.from-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3404)
The member *impls-for*<schedule_from_t>::*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*<schedule_from_t>::*get-state* is initialized with a callable object equivalent to the following lambda:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*)requires [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)> {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<schedule_result_t<sched_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<class Sndr, class... Env>
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<schedule_result_t<*data-type*<Sndr>>, *FWD-ENV-T*(Env)...>();auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();*decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](#expos "33.9.2Exposition-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*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)>[.](#exec.schedule.from-9.sentence-1)
Let *as-tuple* be an alias template such that*as-tuple*<Tag(Args...)> denotes
the type *decayed-tuple*<Tag, Args...>, and
let *is-nothrow-decay-copy-sig* be a variable template such thatauto(*is-nothrow-decay-copy-sig*<Tag(Args...
)>) is
a constant expression of type bool and
equal to (is_nothrow_constructible_v<decay_t<Args>, 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*<Sigs> &&...) is false,
and an empty pack otherwise[.](#exec.schedule.from-9.sentence-3)
Then variant_t denotes
the type variant<monostate, *as-tuple*<Sigs>..., *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]<class Tuple>(Tuple& result) noexcept -> void {if constexpr (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<monostate, Tuple>) {auto& [tag, ...args] = result;
tag(std::move(*state*->*rcvr*), std::move(args)...); }}, *state*->*async-result*); }template<class Error>void set_error(Error&& err) && noexcept { execution::set_error(std::move(*state*->*rcvr*), std::forward<Error>(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*<schedule_from_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept-> void {using result_t = *decayed-tuple*<Tag, Args...>; constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<Args>, Args> && ...); try { state.*async-result*.template emplace<result_t>(Tag(), std::forward<Args>(args)...); } catch (...) {if constexpr (!nothrow) state.*async-result*.template emplace<tuple<set_error_t,
exception_ptr>>(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.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> 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.6Schedulers[exec.sched]"), or
- [(2.2)](#exec.on-2.2)
decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") andsndr is not
a pipeable sender adaptor closure object ([[exec.adapt.obj]](#exec.adapt.obj "33.9.12.2Closure objects")), or
- [(2.3)](#exec.on-2.3)
decltype((sndr)) satisfies [sender](#concept:sender "33.9.3Sender 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.3Sender 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.6Schedulers[exec.sched]"), or
- [(4.2)](#exec.on-4.2)
decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3Sender 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.2Closure 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.3Sender concepts[exec.snd.concepts]")<OutSndr, on_t> 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.6Schedulers[exec.sched]")<decltype(data)>) {return *JOIN-ENV*(*SCHED-ENV*(std::forward_like<OutSndr>(data)), *FWD-ENV*(std::forward<Env>(env)));} else {return std::forward<Env>(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.6Schedulers[exec.sched]")<decltype(data)>) {auto orig_sch =*query-with-default*(get_scheduler, env, *not-a-scheduler*()); if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<decltype(orig_sch), *not-a-scheduler*>) {return *not-a-sender*{}; } else {return continues_on( starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)),
std::move(orig_sch)); }} else {auto& [sch, closure] = data; auto orig_sch = *query-with-default*( get_completion_scheduler<set_value_t>,
get_env(child), *query-with-default*(get_scheduler, env, *not-a-scheduler*())); if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<decltype(orig_sch), *not-a-scheduler*>) {return *not-a-sender*{}; } else {return write_env( continues_on( std::forward_like<OutSndr>(closure)( continues_on( write_env(std::forward_like<OutSndr>(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.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> 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.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> 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<set_value_t>(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.3Sender concepts[exec.snd.concepts]"), ordecltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1General[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.2Exposition-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* =[]<class Tag, class... Args>(auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, *decayed-typeof*<set-cpo>>) {*TRY-SET-VALUE*(rcvr,
invoke(std::move(fn), std::forward<Args>(args)...)); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}; template<class Sndr, class... Env>static consteval void *check-types*(); };}
[🔗](#lib:check-types,impls-for%3cdecayed-typeof%3cthen-cpo%3e%3e)
`template<class Sndr, class... Env>
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*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {if constexpr (![invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<remove_cvref_t<*data-type*<Sndr>>, 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.3Sender concepts[exec.snd.concepts]") or
if decltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1General[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.2Concept 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.2Exposition-only entities"))
is specialized for *let-cpo* as follows:
[🔗](#lib:impls-for%3cdecayed-typeof%3clet-cpo%3e%3e)
namespace std::execution {template<class State, class Rcvr, class... Args>void *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*; template<class Sndr, class... Env>static 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 {template<class Rcvr, class Env>struct *receiver2* {using receiver_concept = receiver_t; template<class... Args>void set_value(Args&&... args) && noexcept { execution::set_value(std::move(*rcvr*), std::forward<Args>(args)...); }template<class Error>void set_error(Error&& err) && noexcept { execution::set_error(std::move(*rcvr*), std::forward<Error>(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.2queryable 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.4Header <execution> 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<class Sndr, class... Env>
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*<Sndr>>;auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(*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.11Concept constructible_­from[concept.constructible]")<decay_t<Ts>, Ts> &&...)
- [(7.2)](#exec.let-7.2)
[invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<LetFn, decay_t<Ts>&...>
- [(7.3)](#exec.let-7.3)
[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<invoke_result_t<LetFn, decay_t<Ts>&...>>
- [(7.4)](#exec.let-7.4)
sizeof...(Env) == 0 || [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<invoke_result_t<LetFn, decay_t<Ts>&...>, *env-t*
...>
where *env-t* is the packdecltype(*let-cpo*.transform_env(declval<Sndr>(), declval<Env>()))[.](#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:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires *see below* {auto& [_, fn, child] = sndr; using fn_t = decay_t<decltype(fn)>; 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<Sndr>(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*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)>[.](#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*<Tag(Args...)> denotes
the type *decayed-tuple*<Args...>[.](#exec.let-9.sentence-3)
Then args_variant_t denotes
the type variant<monostate, *as-tuple*<LetSigs>...> 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*<Tag(Args...)> denotes
the type *call-result-t*<F, decay_t<Args>&...>[.](#exec.let-10.sentence-1)
Then ops2_variant_t denotes
the typevariant<monostate, connect_result_t<*as-sndr2*<LetSigs>, *receiver2*<Rcvr, env_t>>...> 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.1Preamble[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*<Args...>;auto mkop2 = [&] {return connect( apply(std::move(state.fn),
state.args.template emplace<args_t>(std::forward<Args>(args)...)), *receiver2*{rcvr, std::move(state.env)});};
start(state.ops2.template emplace<decltype(mkop2())>(*emplace-from*{mkop2}));
[13](#exec.let-13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4059)
*impls-for*<*decayed-typeof*<let-cpo>>::*complete* is initialized with a callable object equivalent to the following:[]<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, *decayed-typeof*<*set-cpo*>>) {*TRY-EVAL*(rcvr, *let-bind*(state, rcvr, std::forward<Args>(args)...)); } else { Tag()(std::move(rcvr), std::forward<Args>(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.3Sender concepts[exec.snd.concepts]")<Sndr, *decayed-typeof*<*let-cpo*>> 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.3Asynchronous 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<decltype(policy)>,Shape be decltype(auto(shape)), andFunc be decay_t<decltype((f))>[.](#exec.bulk-2.sentence-3)
If
- [(2.1)](#exec.bulk-2.1)
decltype((sndr)) does not satisfy [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), or
- [(2.2)](#exec.bulk-2.2)
is_execution_policy_v<Policy> is false, or
- [(2.3)](#exec.bulk-2.3)
Shape does not satisfy [integral](concepts.arithmetic#concept:integral "18.4.7Arithmetic concepts[concepts.arithmetic]"), or
- [(2.4)](#exec.bulk-2.4)
Func does not model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14Concept 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.14Concept 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.3Sender concepts[exec.snd.concepts]")<Sndr, bulk_t> 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.2Exposition-only entities"))
is specialized for bulk_chunked_t as follows:
[🔗](#lib:impls-for%3cbulk_chunked_t%3e)
namespace std::execution {template<>struct *impls-for*<bulk_chunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
The member *impls-for*<bulk_chunked_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, set_value_t>) {auto& [policy, shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) { f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
Tag()(std::move(rcvr), std::forward<Args>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1Preamble[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.2Exposition-only entities"))
is specialized for bulk_unchunked_t as follows:namespace std::execution {template<>struct *impls-for*<bulk_unchunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; };}
The member *impls-for*<bulk_unchunked_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, set_value_t>) {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>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1Preamble[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<class Sndr, class... Env>
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*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {if constexpr (![invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<remove_cvref_t<*data-type*<Sndr>>, 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.3Asynchronous 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.14Concept 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<e and
for every i of type Shape from 0 to shape,
there is exactly one invocation with a pair b and e,
such that i is in the range [b, e)[.](#exec.bulk-8.1.1.3.sentence-1)
* [(8.1.2)](#exec.bulk-8.1.2)
If out_sndr completes with set_error(rcvr, eptr), then
the asynchronous operation may invoke a subset of
the invocations of f before the error completion handler is called, and eptr is an exception_ptr containing either:
+
[(8.1.2.1)](#exec.bulk-8.1.2.1)
an exception thrown by an invocation of f, or
+
[(8.1.2.2)](#exec.bulk-8.1.2.2)
a bad_alloc exception if
the implementation fails to allocate required resources, or
+
[(8.1.2.3)](#exec.bulk-8.1.2.3)
an exception derived from runtime_error[.](#exec.bulk-8.1.2.sentence-1)
* [(8.1.3)](#exec.bulk-8.1.3)
If out_sndr completes with set_stopped(rcvr), then
the asynchronous operation may invoke a subset of
the invocations of f before the stopped completion handler[.](#exec.bulk-8.1.3.sentence-1)
- [(8.2)](#exec.bulk-8.2)
If sndr does not complete with set_value, then
the completion is forwarded to recv[.](#exec.bulk-8.2.sentence-1)
- [(8.3)](#exec.bulk-8.3)
For *bulk-algo*,
the parameter policy describes
the manner in which
the execution of the asynchronous operations corresponding to these algorithms
may be parallelized and
the manner in which
they apply f[.](#exec.bulk-8.3.sentence-1)
Permissions and requirements
on parallel algorithm element access functions ([[algorithms.parallel.exec]](algorithms.parallel.exec "26.3.3Effect of execution policies on algorithm execution"))
apply to f[.](#exec.bulk-8.3.sentence-2)
[9](#exec.bulk-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4358)
[*Note [2](#exec.bulk-note-2)*:
The asynchronous operation corresponding to*bulk-algo*(sndr, policy, shape, f) can complete with set_stopped if cancellation is requested or
ignore cancellation requests[.](#exec.bulk-9.sentence-1)
— *end note*]
#### [33.9.12.12](#exec.when.all) execution::when_all [[exec.when.all]](exec.when.all)
[1](#exec.when.all-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4368)
when_all and when_all_with_variant both adapt multiple input senders into a sender
that completes when all input senders have completed[.](#exec.when.all-1.sentence-1)
when_all only accepts senders
with a single value completion signature and
on success concatenates all the input senders' value result datums
into its own value completion operation[.](#exec.when.all-1.sentence-2)
when_all_with_variant(sndrs...) is semantically equivalent to
when_all(into_variant(sndrs)...),
where sndrs is a pack of subexpressions
whose types model [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#exec.when.all-1.sentence-3)
[2](#exec.when.all-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4381)
The names when_all and when_all_with_variant denote
customization point objects[.](#exec.when.all-2.sentence-1)
Let sndrs be a pack of subexpressions,
let Sndrs be a pack of the types decltype((sndrs))..., and
let CD be
the type common_type_t<decltype(*get-domain-early*(sndrs))...>[.](#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.3Sender concepts[exec.snd.concepts]")<Sndrs> && ...) 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.2Exposition-only entities"))
is specialized for when_all_t as follows:
[🔗](#lib:impls-for%3cwhen_all_t%3e)
namespace std::execution {template<>struct *impls-for*<when_all_t> : *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*; template<class Sndr, class... Env>static 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:template<class Env>constexpr 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.2queryable 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*<Env> denotes the typedecltype(*make-
when-all-env*(declval<inplace_stop_source&>(), declval<Env>()))[.](#exec.when.all-6.sentence-1)
[🔗](#lib:check-types,impls-for%3cwhen_all_t%3e)
`template<class Sndr, class... Env>
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*<Sndr>[.](#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 = []<class Child>() {auto cs = get_completion_signatures<Child, *when-all-env*<Env>...>(); if constexpr (cs.*count-of*(set_value) >= 2)throw *unspecified-exception*(); *decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](#expos "33.9.2Exposition-only entities")};(fn.template operator()<*child-type*<Sndr, Is>>(), ...); 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*<when_all_t>::*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.2Concept same_­as[concept.same]")<CD, default_domain>) {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*<when_all_t>::*get-env* is initialized with a callable object
equivalent to the following lambda expression:[]<class State, class Rcvr>(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*<when_all_t>::*get-state* is initialized with a callable object
equivalent to the following lambda expression:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {return e;} where e is the expressionstd::forward<Sndr>(sndr).*apply*(*make-state*<Rcvr>()) and where *make-state* is the following exposition-only class template:enum class *disposition* { *started*, *error*, *stopped* }; // *exposition only*template<class Rcvr>struct *make-state* {template<class... Sndrs>auto operator()(auto, auto, Sndrs&&... sndrs) const {using values_tuple = *see below*; using errors_variant = *see below*; using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, *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<size_t> *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<stop_callback> *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<value_types_of_t<Sndrs, *FWD-ENV-T*(env_of_t<Rcvr>), *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 = []<class... T>(tuple<T...>& t) noexcept { return tuple<T&...>(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([&]<class Error>(Error& error) noexcept {if constexpr (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Error, *none-such*>) { 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*<when_all_t>::*start* is initialized with a callable object
equivalent to the following lambda expression:[]<class State, class Rcvr, class... Ops>( 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<when_all_t>::*complete** is initialized with a callable object
equivalent to the following lambda expression:[]<class Index, class State, class Rcvr, class Set, class... Args>(this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Set, set_error_t>) {if (*disposition*::*error* != state.disp.exchange(*disposition*::*error*)) { state.*stop_src*.request_stop(); *TRY-EMPLACE-ERROR*(state.errors, std::forward<Args>(args)...); }} else if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Set, set_stopped_t>) {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.2Concept same_­as[concept.same]")<decltype(State::values), tuple<>>) {if (state.disp == *disposition*::*started*) {auto& opt = get<Index::value>(state.values); *TRY-EMPLACE-VALUE*(complete, opt, std::forward<Args>(args)...); }} state.*arrive*(rcvr);} where *TRY-EMPLACE-ERROR*(v, e),
for subexpressions v and e, is equivalent to:try { v.template emplace<decltype(auto(e))>(e);} catch (...) { v.template emplace<exception_ptr>(current_exception());} if the expression decltype(auto(e))(e) is potentially throwing;
otherwise, v.template emplace<decltype(auto(e))>(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*<decltype(as)...>{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.3Sender concepts[exec.snd.concepts]")<decltype((sndr)), when_all_with_variant_t> 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<decltype((sndr))>(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.3Sender 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.2Exposition-only entities"))
is specialized for into_variant as follows:
[🔗](#lib:impls-for%3cinto_variant_t%3e)
namespace std::execution {template<>struct *impls-for*<into_variant_t> : *default-impls* {static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*() {auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>(); *decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](#expos "33.9.2Exposition-only entities")}};}
[5](#exec.into.variant-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4762)
The member *impls-for*<into_variant_t>::*get-state* is initialized with a callable object equivalent to the following lambda:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept-> type_identity<value_types_of_t<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)>> {return {};}
[6](#exec.into.variant-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4772)
The member *impls-for*<into_variant_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class State, class Rcvr, class Tag, class... Args>(auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, set_value_t>) {using variant_type = typename State::type; *TRY-SET-VALUE*(rcvr, variant_type(*decayed-tuple*<Args...>{std::forward<Args>(args)...})); } else { Tag()(std::move(rcvr), std::forward<Args>(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.2Exposition-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*<stopped_as_optional_t> : *default-impls* {template<class Sndr, class... Env>static consteval void *check-types*() {*default-impls*::*check-types*<Sndr, Env...>(); if constexpr (!requires {requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<void, *single-sender-value-type*<*child-type*<Sndr>, *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.3Sender concepts[exec.snd.concepts]")<Sndr, stopped_as_optional_t> is false then the expression stopped_as_optional.transform_sender(sndr, env) is ill-formed;
otherwise,
if [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*child-type*<Sndr>, *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*<Sndr>, *FWD-ENV-T*(Env)>;return let_stopped( then(std::forward_like<Sndr>(child), []<class... Ts>(Ts&&... ts) noexcept(is_nothrow_constructible_v<V, Ts...>) {return optional<V>(in_place, std::forward<Ts>(ts)...); }), []() noexcept { return just(optional<V>()); });
#### [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.3Sender concepts[exec.snd.concepts]") or
if the type Err does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1General[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.3Sender concepts[exec.snd.concepts]")<Sndr, stopped_as_error_t> 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<Sndr>(child), [err = std::forward_like<Sndr>(err)]() mutable noexcept(is_nothrow_move_constructible_v<E>) {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.1Execution scope concepts[exec.scope.concepts]") Token, [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>struct *associate-data* { // *exposition only*using *wrap-sender* = // *exposition only* remove_cvref_t<decltype(declval<Token&>().wrap(declval<Sender>()))>; explicit *associate-data*(Token t, Sender&& s): *sndr*(t.wrap(std::forward<Sender>(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<pair<Token, *wrap-sender*>> 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.1Execution scope concepts[exec.scope.concepts]") Token, [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>*associate-data*(Token, Sender&&) -> *associate-data*<Token, Sender>;}
[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<wrap-sender> &&
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.14Concept 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<wrap-sender>);
`
[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<pair<Token, wrap-sender>>
release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
`
[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<Token, *wrap-sender*> 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.3Sender concepts[exec.snd.concepts]"), orremove_cvref_t<decltype((token))> does not satisfy [scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution 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.2Exposition-only entities"))
is specialized for associate_t as follows:
[🔗](#lib:execution::impls-for%3cassociate_t%3e)
namespace std::execution {template<>struct *impls-for*<associate_t> : *default-impls* {static constexpr auto *get-state* = *see below*; // *exposition only*static constexpr auto *start* = *see below*; // *exposition only*template<class Sndr, class... Env>static consteval void *check-types*() { // *exposition only*using associate_data_t = remove_cvref_t<*data-type*<Sndr>>; using child_type_t = typename associate_data_t::*wrap-sender*; (void)get_completion_signatures<child_type_t, *FWD-ENV-T*(Env)...>(); }};}
[12](#exec.associate-12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5066)
The member *impls-for*<associate_t>::*get-state* is initialized with a callable object equivalent to the following lambda:
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*) {auto [_, data] = std::forward<Sndr>(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<wrap_sender, Rcvr>; 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*<associate_t>::*get-state* isis_nothrow_constructible_v<remove_cvref_t<Sndr>, Sndr> && is_nothrow_move_constructible_v<*wrap-sender*> &&[*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2Header <functional> synopsis[functional.syn]")<connect_t, *wrap-sender*, Rcvr> where *wrap-sender* is the typeremove_cvref_t<*data-type*<Sndr>>::*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*<associate_t>::*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.3Sender concepts[exec.snd.concepts]"), orremove_cvref_t<decltype((token))> does not satisfy [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3Stop 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<decltype((token))> models[unstoppable_token](stoptoken.concepts#concept:unstoppable_token "32.3.3Stop 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.3Stop 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.3Stop 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.2Concept invocable[concept.invocable]")<Fn> and [constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<Fn, Init> are modeled, *stoken-t*::callback_type<Fn> models [*stoppable-callback-for*](stoptoken.concepts#concept:stoppable-callback-for "32.3.3Stop token concepts[stoptoken.concepts]")<Fn, *stoken-t*, Init>[.](#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<Fn>(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<decltype((token))>, and
- [(2.3)](#exec.spawn.future-2.3)
let Env be remove_cvref_t<decltype((env))>[.](#exec.spawn.future-2.sentence-2)
If any of[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr>,[scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]")<Token>, or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")<Env> 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 {template<class Completions>struct *spawn-future-state-base*; // *exposition only*template<class... Sigs>struct *spawn-future-state-base*<completion_signatures<Sigs...>> { // *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*<Tag, Args...>[.](#exec.spawn.future-4.sentence-2)
- [(4.1)](#exec.spawn.future-4.1)
If is_nothrow_constructible_v<decay_t<Arg>, 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<monostate, tuple<set_stopped_t>, *as-tuple*<Sigs>...>,
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<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, *as-tuple*<Sigs>...>,
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 {template<class Completions>struct *spawn-future-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-future-state-base*<Completions>* *state*; // *exposition only*template<class... T>void set_value(T&&... t) && noexcept {*set-complete*<set_value_t>(std::forward<T>(t)...); }template<class E>void set_error(E&& e) && noexcept {*set-complete*<set_error_t>(std::forward<E>(e)); }void set_stopped() && noexcept {*set-complete*<set_stopped_t>(); }private:template<class CPO, class... T>void *set-complete*(T&&... t) noexcept { // *exposition only*constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...); try {*state*->*result*.template emplace<*decayed-tuple*<CPO, T...>>(CPO{},
std::forward<T>(t)...); }catch (...) {if constexpr (!nothrow) {using tuple_t = *decayed-tuple*<set_error_t, exception_ptr>; *state*->*result*.template emplace<tuple_t>(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.3Stop 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.3Sender concepts[exec.snd.concepts]") Sender, class Env>using *future-spawned-sender* = // *exposition only*decltype(write_env(*stop-when*(declval<Sender>(), declval<*stoken-t*>()), declval<Env>()));
[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 {template<class Alloc, [scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") Token, [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender, class Env>struct *spawn-future-state* // *exposition only*: *spawn-future-state-base*<completion_signatures_of_t<*future-spawned-sender*<Sender, Env>>> {using *sigs-t* = // *exposition only* completion_signatures_of_t<*future-spawned-sender*<Sender, Env>>; using *receiver-t* = // *exposition only**spawn-future-receiver*<*sigs-t*>; using *op-t* = // *exposition only* connect_result_t<*future-spawned-sender*<Sender, Env>, *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<Sender>(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.1Receiver concepts[exec.recv.concepts]") auto& rcvr) noexcept; // *exposition only*void *abandon*() noexcept; // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits<Alloc>::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.2Multi-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.1Receiver 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.2Concept same_­as[concept.same]")<remove_reference_t<decltype(tuple)>, 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.2Exposition-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*<spawn_future_t> : *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*<spawn_future_t>::*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<void>() 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<execution::[sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*, *decayed-tuple*,
type_identity_t>>; template<execution::[sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-with-variant-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*>>;}
[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.3Sender concepts[exec.snd.concepts]")<Sndr, *sync-wait-env*> is true[.](#exec.sync.wait-4.1.sentence-1)
- [(4.2)](#exec.sync.wait-4.2)
The type *sync-wait-result-type*<Sndr> 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.2Concept same_­as[concept.same]")<decltype(e), *sync-wait-result-type*<Sndr>> 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 {template<class Sndr>struct *sync-wait-state* { // *exposition only* execution::run_loop *loop*; // *exposition only* exception_ptr *error*; // *exposition only**sync-wait-result-type*<Sndr> *result*; // *exposition only*}; template<class Sndr>struct *sync-wait-receiver* { // *exposition only*using receiver_concept = execution::receiver_t; *sync-wait-state*<Sndr>* *state*; // *exposition only*template<class... Args>void set_value(Args&&... args) && noexcept; template<class Error>void 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<class... Args>
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>(args)...);} catch (...) {*state*->*error* = current_exception();}*state*->*loop*.finish();
[🔗](#exec.sync.wait-itemdecl:2)
`template<class Error>
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<Error>(err)); // see [[exec.general]](exec.general "33.1General")*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.3Sender concepts[exec.snd.concepts]")<Sndr, *sync-wait-receiver*<Sndr>> is false,
the expression sync_wait.apply_sender(sndr) is ill-formed;
otherwise, it is equivalent to:*sync-wait-state*<Sndr> state;auto op = connect(sndr, *sync-wait-receiver*<Sndr>{&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.6block"))
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3Forward 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.3Sender concepts[exec.snd.concepts]")<Sndr, *sync-wait-env*> 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*<Sndr> 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.2Concept same_­as[concept.same]")<decltype(e), *sync-wait-with-variant-result-type*<Sndr>> 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*<Sndr>;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.6block"))
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3Forward 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<decltype((token))>, and
- [(2.3)](#exec.spawn-2.3)
let Env be remove_cvref_t<decltype((env))>[.](#exec.spawn-2.sentence-2)
If any of[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr>,[scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]")<Token>, or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")<Env> 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 {template<class Alloc, [scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") Token, [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>struct *spawn-state* : *spawn-state-base* { // *exposition only*using *op-t* = connect_result_t<Sender, *spawn-receiver*>; // *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<Alloc>::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<void>() 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)