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

2143 lines
110 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.adapt]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.adapt)
### 33.9.12 Sender adaptors [exec.adapt]
#### [33.9.12.1](#general) General [[exec.adapt.general]](exec.adapt.general)
[1](#general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3002)
Subclause [exec.adapt] specifies a set of sender adaptors[.](#general-1.sentence-1)
[2](#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[.](#general-2.sentence-1)
The adaptors also support function call syntax with equivalent semantics[.](#general-2.sentence-2)
[3](#general-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3010)
Unless otherwise specified:
- [(3.1)](#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[.](#general-3.1.sentence-1)
- [(3.2)](#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"))[.](#general-3.2.sentence-1)
- [(3.3)](#general-3.3)
A parent sender with more than one child sender has
an associated attributes object equal to env<>{}[.](#general-3.3.sentence-1)
- [(3.4)](#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))[.](#general-3.4.sentence-1)
- [(3.5)](#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[.](#general-3.5.sentence-1)
- [(3.6)](#general-3.6)
These requirements apply to any function
that is selected by the implementation of the sender adaptor[.](#general-3.6.sentence-1)
- [(3.7)](#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[.](#general-3.7.sentence-1)
[4](#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]
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[.](#general-4.sentence-1)
#### [33.9.12.2](#obj) Closure objects [[exec.adapt.obj]](exec.adapt.obj)
[1](#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](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") arguments and returns a [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#obj-1.sentence-1)
For a pipeable sender adaptor closure object c and
an expression sndr such that decltype((sndr)) models [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"),
the following expressions are equivalent and yield a [sender](exec.snd.concepts#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)](#obj-1.1)
Its target object is an object d2 of type decltype(auto(d)) direct-non-list-initialized with d[.](#obj-1.1.sentence-1)
- [(1.2)](#obj-1.2)
It has one bound argument entity,
an object c2 of type decltype(auto(c)) direct-non-list-initialized with c[.](#obj-1.2.sentence-1)
- [(1.3)](#obj-1.3)
Its call pattern is d2(c2(arg)),
where arg is the argument used in a function call expression of e[.](#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[.](#obj-1.sentence-5)
[2](#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](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#obj-2.sentence-1)
[3](#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[.](#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>>[.](#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[.](#obj-3.sentence-3)
[4](#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](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") as its first argument and
returns a [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#obj-4.sentence-1)
If a pipeable sender adaptor object accepts only one argument,
then it is a pipeable sender adaptor closure object[.](#obj-4.sentence-2)
[5](#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](exec.snd.concepts#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))...[.](#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)](#obj-5.1)
Its target object is a copy of adaptor[.](#obj-5.1.sentence-1)
- [(5.2)](#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[.](#obj-5.2.sentence-1)
- [(5.3)](#obj-5.3)
Its call pattern is adaptor(rcvr, bound_args...),
where rcvr is
the argument used in a function call expression of f[.](#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[.](#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](exec.snd.concepts#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]](exec.snd.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](exec.snd.concepts#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*](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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]](exec.snd.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*](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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]](exec.snd.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](exec.snd.concepts#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]](exec.snd.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](exec.snd.concepts#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](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") andsndr is not
a pipeable sender adaptor closure object ([[exec.adapt.obj]](#obj "33.9.12.2Closure objects")), or
- [(2.3)](#exec.on-2.3)
decltype((sndr)) satisfies [sender](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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]](#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*](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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]](exec.snd.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](exec.snd.concepts#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]](exec.snd.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](exec.snd.concepts#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](exec.snd.concepts#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*](exec.snd.concepts#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](exec.snd.concepts#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*](exec.snd.concepts#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]](exec.snd.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]](exec.snd.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](exec.snd.concepts#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](exec.snd.concepts#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]](exec.snd.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]](exec.snd.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*](exec.snd.concepts#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](exec.snd.concepts#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]](exec.snd.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]](exec.snd.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]](exec.snd.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*](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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*](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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]](exec.snd.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](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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](exec.snd.concepts#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]](exec.snd.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)