, Env>` is
- satisfied. Then sender-of-in<schedule_result_t<Sch>,
+ satisfied. Then sender-in-of<schedule_result_t<Sch>,
Env>
shall be modeled.
3. None of a scheduler's copy constructor, destructor, equality comparison, or
@@ -6360,21 +6360,21 @@ namespace std::execution {
3. This subclause makes use of the following exposition-only entities.
1. For a queryable object `env`, FWD-ENV(env)
is an
- expression whose type satisfies *`queryable`* such that for a query object
+ expression whose type satisfies `queryable` such that for a query object
`q` and a pack of subexpressions `as`, the expression
FWD-ENV(env).query(q, as...)
is ill-formed if
`forwarding_query(q)` is `false`; otherwise, it is expression-equivalent
to `env.query(q, as...)`.
2. For a query object `q` and a subexpression `v`, MAKE-ENV(q,
- v)
is an expression `env` whose type satisfies *`queryable`* such
+ v)
is an expression `env` whose type satisfies `queryable` such
that the result of `env.query(q)` has a value equal to `v`
([concepts.equality]). Unless otherwise stated, the object to which
`env.query(q)` refers remains valid while `env` remains valid.
3. For two queryable objects `env1` and `env2`, a query object `q` and a
pack of subexpressions `as`, JOIN-ENV(env1, env2)
is
- an expression `env3` whose type satisfies *`queryable`* such that
+ an expression `env3` whose type satisfies `queryable` such that
`env3.query(q, as...)` is expression-equivalent to:
- `env1.query(q, as...)` if that expression is well-formed,
@@ -6390,13 +6390,13 @@ namespace std::execution {
arguments.
5. For a scheduler `sch`, SCHED-ATTRS(sch)
is an
- expression `o1` whose type satisfied *`queryable`* such that
+ expression `o1` whose type satisfies `queryable` such that
o1.query(get_completion_scheduler<Tag>)
is a
expression with the same type and value as `sch` where `Tag` is
one of `set_value_t` or `set_stopped_t`, and such that
o1.query(get_domain)
is expression-equivalent to
sch.query(get_domain)
. SCHED-ENV(sch)
- is an expression `o2` whose type satisfied *`queryable`* such that
+ is an expression `o2` whose type satisfies `queryable` such that
o1.query(get_scheduler)
is a prvalue with the same type and
value as `sch`, and such that o2.query(get_domain)
is
expression-equivalent to sch.query(get_domain)
.
@@ -6460,8 +6460,8 @@ namespace std::execution {
constexpr auto get-domain-early(const Sndr& sndr) noexcept;
- 1. Effects: Equivalent to: return Domain();
- where `Domain` is the decayed type of the first of the
+ 1. Effects: Equivalent to: return Domain();
+ where `Domain` is the decayed type of the first of the
following expressions that is well-formed:
- `get_domain(get_env(sndr))`
@@ -6492,14 +6492,14 @@ namespace std::execution {
with `schedule_from` ([exec.schedule.from])) to give scheduler
authors a way to customize both how to transition onto
(`transfer`) and off of (`schedule_from`) a given execution
- context. Thus, `transfer` must ignore the domain of the
- predecessor and use the domain of the destination scheduler to
- select a customization, a property that is unique to `transfer`.
- That is why it is given special treatment here.
+ context. Thus, `transfer` ignores the domain of the predecessor
+ and uses the domain of the destination scheduler to select a
+ customization, a property that is unique to `transfer`. That is
+ why it is given special treatment here.
- - Otherwise, return Domain();
where `Domain` is
+ - Otherwise, return Domain();
where `Domain` is
the first of the following expressions that is well-formed and
- its type is not `void`:
+ whose type is not `void`:
- `get_domain(get_env(sndr))`
@@ -6540,17 +6540,19 @@ namespace std::execution {
13.
- template<class... T>
+ template<class T0, class T1, ... class Tn>
struct product-type { // exposition only
- using type0 = T0; // exposition only
- using type1 = T1; // exposition only
- ...
- using typen-1 = Tn-1; // exposition only
-
T0 t0; // exposition only
T1 t1; // exposition only
...
- Tn-1 tn-1; // exposition only
+ Tn tn; // exposition only
+
+ template<size_t I, class Self>
+ constexpr decltype(auto) get(this Self&& self) noexcept; // exposition only
+
+ template<class Self, class Fn>
+ constexpr decltype(auto) apply(this Self&& self, Fn&& fn) // exposition only
+ noexcept(see below);
};
@@ -6558,7 +6560,35 @@ namespace std::execution {
`product-type` is usable as the initializer of a
structured binding declaration [dcl.struct.bind].
-
+ 2.
+ template<size_t I, class Self>
+ constexpr decltype(auto) get(this Self&& self) noexcept;
+
+
+ 1. *Effects:* Equivalent to:
+
+
+ auto& [...ts] = self;
+ return std::forward_like<Self>(ts...\[I]);
+
+
+ 3.
+ template<class Self, class Fn>
+ constexpr decltype(auto) apply(this Self&& self, Fn&& fn) noexcept(see below);
+
+
+ 1. *Effects:* Equivalent to:
+
+
+ auto& [...ts] = self;
+ return std::forward<Fn>(fn)(std::forward_like<Self>(ts)...);
+
+
+ 2. *Requires:* The expression in the `return` statement above is
+ well-formed.
+
+ 4. *Remarks:* The expression in the `noexcept` clause is `true` if the
+ `return` statement above is not potentially throwing; otherwise, `false`.
14.
template<class Tag, class Data = see below, class... Child>
@@ -6574,11 +6604,8 @@ namespace std::execution {
- `(sender &&...)`
2. *Returns:* A prvalue of type basic-sender<Tag,
- decay_t<Data>, decay_t<Child>...>
where the
- `tag` member has been default-initialized and the
- `data` and
- childn...
members have been direct
- initialized from their respective forwarded arguments, where
+ decay_t<Data>, decay_t<Child>...> that has been
+ direct-list-initialized with the forwarded arguments, where
`basic-sender` is the following exposition-only
class template except as noted below:
@@ -6589,7 +6616,7 @@ namespace std::execution {
same_as<Tag, set_value_t> || same_as<Tag, set_error_t> || same_as<Tag, set_stopped_t>;
template<template<class...> class T, class... Args>
- concept well-formed = requires { typename T<Args...>; }; // exposition only
+ concept valid-specialization = requires { typename T<Args...>; }; // exposition only
struct default-impls { // exposition only
static constexpr auto get-attrs = see below;
@@ -6609,16 +6636,13 @@ namespace std::execution {
template<class Index, class Sndr, class Rcvr> // exposition only
using env-type = call-result-t<
decltype(impls-for<tag_of_t<Sndr>>::get-env), Index,
- state-type<Sndr, Rcvr>&, const Rcvr&>>;
+ state-type<Sndr, Rcvr>&, const Rcvr&>;
- template<class Sndr>
- using data-type = decltype((declval<Sndr>().data)); // exposition only
-
- template<class Sndr, size_t N = 0>
- using child-type = decltype((declval<Sndr>().childN)); // exposition only
+ template<class Sndr, size_t I = 0>
+ using child-type = decltype(declval<Sndr>().template get<I+2>()); // exposition only
template<class Sndr>
- using indices-for = typename remove_reference_t<Sndr>::indices-for; // exposition only
+ using indices-for = remove_reference_t<Sndr>::indices-for; // exposition only
template<class Sndr, class Rcvr>
struct basic-state { // exposition only
@@ -6631,7 +6655,7 @@ namespace std::execution {
};
template<class Sndr, class Rcvr, class Index>
- requires well-formed<env-type, Index, Sndr, Rcvr>
+ requires valid-specialization<env-type, Index, Sndr, Rcvr>
struct basic-receiver { // exposition only
using receiver_concept = receiver_t;
@@ -6670,8 +6694,8 @@ namespace std::execution {
decltype(connect-all), basic-state<Sndr, Rcvr>*, Sndr, indices-for<Sndr>>;
template<class Sndr, class Rcvr>
- requires well-formed<state-type, Sndr, Rcvr> &&
- well-formed<connect-all-result, Sndr, Rcvr>
+ requires valid-specialization<state-type, Sndr, Rcvr> &&
+ valid-specialization<connect-all-result, Sndr, Rcvr>
struct basic-operation : basic-state<Sndr, Rcvr> { // exposition only
using operation_state_concept = operation_state_t;
using tag-t = tag_of_t<Sndr>; // exposition only
@@ -6690,15 +6714,16 @@ namespace std::execution {
};
template<class Sndr, class Env>
- using completion-signatures-for = see below; // exposition only
+ using completion-signatures-for = see below; // exposition only
template<class Tag, class Data, class... Child>
- struct basic-sender { // exposition only
+ struct basic-sender : product-type<Tag, Data, Child...> { // exposition only
using sender_concept = sender_t;
using indices-for = index_sequence_for<Child...>; // exposition only
decltype(auto) get_env() const noexcept {
- return impls-for<Tag>::get-attrs(data, child0, ... childn-1);
+ auto& [_, data, ...child] = *this;
+ return impls-for<Tag>::get-attrs(data, child...);
}
template<decays-to<basic-sender> Self, receiver Rcvr>
@@ -6712,26 +6737,19 @@ namespace std::execution {
-> completion-signatures-for<Self, Env> {
return {};
}
-
- Tag tag; // exposition only
- Data data; // exposition only
- Child0 child0; // exposition only
- Child1 child1; // exposition only
- ...
- Childn-1 childn-1; // exposition only
};
}
1. *Remarks:* The default template argument for the `Data` template parameter
- denotes an unspecified empty trivial class type.
+ denotes an unspecified empty trivially copyable class type that models
+ `semiregular`.
- 2. It is unspecified whether instances of `basic-sender` can be
- aggregate initialized.
+ 2. It is unspecified whether a specialization of `basic-sender` is
+ an aggregate.
- 3. An expression of type
- `basic-sender` is usable as the initializer of a
- structured binding declaration [dcl.struct.bind].
+ 3. An expression of type `basic-sender` is usable as the initializer of a
+ structured binding declaration [dcl.struct.bind].
4. The expression in the `noexcept` clause of the constructor of `basic-state`
is:
@@ -6741,30 +6759,30 @@ namespace std::execution {
nothrow-callable<decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&>
- 5. The object `connect-all` is initialized with the following
- lambda:
+ 5. The object `connect-all` is initialized with a callable object
+ equivalent to the following lambda:
[]<class Sndr, class Rcvr, size_t... Is>(
- basic-state<Sndr, Rcvr>* op, Sndr&& sndr, index_sequence<Is...>) noexcept(noexcept(E))
- -> decltype(E) {
- return E;
+ basic-state<Sndr, Rcvr>* op, Sndr&& sndr, index_sequence<Is...>) noexcept(see below)
+ -> decltype(auto) {
+ auto& [_, data, ...child] = sndr;
+ return product-type{connect(
+ std::forward_like<Sndr>(child),
+ basic-receiver<Sndr, Rcvr, integral_constant<size_t, Is>>{op})...};
}
- where `E` is the following expression:
+ 1. *Requires:* The expression in the `return` statement is well-formed.
-
- product-type{connect(
- std::forward<Sndr>(sndr).childIs,
- basic-receiver<Sndr, Rcvr, integral_constant<size_t, Is>>{op})...}
-
+ 2. *Remarks:* The expression in the `noexcept` clause is `true` if
+ the `return` statement is not potentially throwing; otherwise, `false`.
6. The expression in the `noexcept` clause of the constructor of `basic-operation`
is:
- is_nothrow_constructible_v<basic-state<Self, Rcvr>, Self, Rcvr> &&
+ is_nothrow_constructible_v<basic-state<Self, Rcvr>, Self, Rcvr> &&
noexcept(connect-all(this, std::forward<Sndr>(sndr), indices-for<Sndr>()))
@@ -6772,7 +6790,7 @@ namespace std::execution {
`basic-sender` is:
- is_nothrow_constructible_v<basic-operation<Self, Rcvr>, Self, Rcvr>
+ is_nothrow_constructible_v<basic-operation<Self, Rcvr>, Self, Rcvr>
8. The member default-impls::get-attrs
is
@@ -6780,7 +6798,7 @@ namespace std::execution {
lambda:
- [](const auto& data, const auto&... child) noexcept -> decltype(auto) {
+ [](const auto&, const auto&... child) noexcept -> decltype(auto) {
if constexpr (sizeof...(child) == 1)
return (FWD-ENV(get_env(child)), ...);
else
@@ -6792,7 +6810,7 @@ namespace std::execution {
with a callable object equivalent to the following lambda:
- []<class Rcvr>(auto index, auto& state, const Rcvr& rcvr) noexcept -> decltype(auto) {
+ [](auto, auto&, const auto& rcvr) noexcept -> decltype(auto) {
return FWD-ENV(get_env(rcvr));
}
@@ -6802,7 +6820,8 @@ namespace std::execution {
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) {
- return (std::forward<Sndr>(sndr).data);
+ auto& [_, data, ...child] = sndr;
+ return std::forward_like<Sndr>(data);
}
@@ -6810,7 +6829,7 @@ namespace std::execution {
with a callable object equivalent to the following lambda:
- [](auto& state, auto& rcvr, auto&... ops) noexcept -> void {
+ [](auto&, auto&, auto&... ops) noexcept -> void {
(execution::start(ops), ...);
}
@@ -6871,12 +6890,12 @@ namespace std::execution {
template<class Sndr>
concept sender =
- bool(enable-sender<remove_cvref_t<Sndr>>) && // atomic constraint
+ bool(enable-sender<remove_cvref_t<Sndr>>) && // atomic constraint ([temp.constr.atomic])
requires (const remove_cvref_t<Sndr>& sndr) {
{ get_env(sndr) } -> queryable;
} &&
- move_constructible<remove_cvref_t<Sndr>> && // rvalues are movable, and
- constructible_from<remove_cvref_t<Sndr>, Sndr>; // lvalues are copyable
+ move_constructible<remove_cvref_t<Sndr>> && // senders are movable and
+ constructible_from<remove_cvref_t<Sndr>, Sndr>; // decay copyable
template<class Sndr, class Env = empty_env>
concept sender_in =
@@ -6897,9 +6916,10 @@ namespace std::execution {
}
-2. Given a subexpression `sndr`, let `Sndr` be `decltype((sndr))`, let `Env` be
- the type of an environment, and let `rcvr` be a receiver with an associated
- environment `Env`. A completion operation is a permissible completion for `Sndr` and `Env` if its
completion signature appears in the argument list of the specialization of
`completion_signatures` denoted by `completion_signatures_of_t`.
@@ -6908,12 +6928,12 @@ namespace std::execution {
starting the resulting operation state are permissible completions for
`Sndr` and `Env`.
-3. A type `Sigs` satisfies and models the exposition-only concept
+3. A type models the exposition-only concept
`valid-completion-signatures` if it denotes a specialization
of the `completion_signatures` class template.
4. The exposition-only concepts `sender-of` and
- `sender-of-in` define the requirements for a sender
+ `sender-in-of` define the requirements for a sender
type that completes with a given unique set of value result types.
@@ -6922,14 +6942,14 @@ namespace std::execution {
using value-signature = set_value_t(As...); // exposition only
template<class Sndr, class Env, class... Values>
- concept sender-of-in =
+ concept sender-in-of =
sender_in<Sndr, Env> &&
MATCHING-SIG( // see [exec.general]
set_value_t(Values...),
value_types_of_t<Sndr, Env, value-signature, type_identity_t>);
template<class Sndr, class... Values>
- concept sender-of = sender-of-in<Sndr, empty_env, Values...>;
+ concept sender-of = sender-in-of<Sndr, empty_env, Values...>;
}
@@ -6971,20 +6991,18 @@ namespace std::execution {
- Only expose an overload of a member `connect` that accepts an lvalue
sender if they model `copy_constructible`.
- - Model `copy_constructible` if they satisfy `copy_constructible`.
-
### Awaitable helpers [exec.awaitables] ### {#spec.exec-awaitables}
-1. The sender concepts recognize awaitables as senders. For this clause
- ([exec]), an awaitable is an expression that would be
+1. The sender concepts recognize awaitables as senders. For [exec], an
+ awaitable is an expression that would be
well-formed as the operand of a `co_await` expression within a given
context.
2. For a subexpression `c`, let GET-AWAITER(c, p)
be
expression-equivalent to the series of transformations and conversions
applied to `c` as the operand of an *await-expression* in a coroutine,
- resulting in lvalue `e` as described by [expr.await]/3.2-4, where `p`
- is an lvalue referring to the coroutine's promise type, `Promise`. `e` as described by [expr.await], where `p`
+ is an lvalue referring to the coroutine's promise, which has type `Promise`. This includes the invocation of the promise type's
`await_transform` member if any, the invocation of the `operator co_await`
picked by overload resolution if any, and any necessary implicit
@@ -7000,7 +7018,7 @@ namespace std::execution {
namespace std {
template<class T>
- concept await-suspend-result = see below;
+ concept await-suspend-result = see below; // exposition only
template<class A, class Promise>
concept is-awaiter = // exposition only
@@ -7047,9 +7065,8 @@ namespace std::execution {
}
template<has-as-awaitable<Derived> T>
- auto await_transform(T&& value)
- noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>())))
- -> decltype(std::forward<T>(value).as_awaitable(declval<Derived&>())) {
+ decltype(auto) await_transform(T&& value)
+ noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>()))) {
return std::forward<T>(value).as_awaitable(static_cast<Derived&>(*this));
}
};
@@ -7217,12 +7234,14 @@ namespace std::execution {
1. `get_completion_signatures` is a customization point object. Let `sndr` be an
expression such that `decltype((sndr))` is `Sndr`, and let `env` be an
expression such that `decltype((env))` is `Env`. Then
- `get_completion_signatures(sndr, env)` is expression-equivalent to:
+ `get_completion_signatures(sndr, env)` is expression-equivalent to
+ (void(sndr), void(env), CS())
except that `void(sndr)`
+ and `void(env)` are indeterminately sequenced, where `CS` is:
- 1. `decltype(sndr.get_completion_signatures(env)){}` if that
- expression is well-formed,
+ 1. `decltype(sndr.get_completion_signatures(env))` if that
+ type is well-formed,
- 2. Otherwise, `remove_cvref_t::completion_signatures{}` if that expression is well-formed,
+ 2. Otherwise, `remove_cvref_t::completion_signatures` if that type is well-formed,
3. Otherwise, if is-awaitable<Sndr, env-promise<Env>>
is `true`, then:
@@ -7232,13 +7251,14 @@ namespace std::execution {
SET-VALUE-SIG(await-result-type<Sndr,
env-promise<Env>>), // see [exec.snd.concepts]
set_error_t(exception_ptr),
- set_stopped_t()>{}
+ set_stopped_t()>
- 4. Otherwise, `get_completion_signatures(sndr, env)` is ill-formed.
+ 4. Otherwise, `CS` is ill-formed.
-2. Let `rcvr` be an rvalue receiver of type `Rcvr`, and let `Sndr` be the type of a
- sender such that `sender_in>` is `true`. Let `Sigs...` be the
+2. Let `rcvr` be an rvalue whose type `Rcvr` models `receiver`, and let `Sndr`
+ be the type of a sender such that `sender_in>` is
+ `true`. Let `Sigs...` be the
template arguments of the `completion_signatures` specialization named by
`completion_signatures_of_t>`. Let `CSO` be
a completion function. If sender `Sndr` or its operation state cause the
@@ -7254,16 +7274,15 @@ namespace std::execution {
2. The name `connect` denotes a customization point object. For subexpressions
`sndr` and `rcvr`, let `Sndr` be `decltype((sndr))` and `Rcvr` be
- `decltype((rcvr))`, and let `DS` and `DR` be the decayed types of `Sndr` and
- `Rcvr`, respectively.
+ `decltype((rcvr))`, and let `DS` and `DR` be `decay_t` and
+ `decay_t`, respectively.
-3. Let `connect-awaitable-promise` be the following class:
+3. Let `connect-awaitable-promise` be the following exposition-only class:
namespace std::execution {
struct connect-awaitable-promise
: with-await-transform<connect-awaitable-promise> {
- DR& rcvr; // exposition only
connect-awaitable-promise(DS&, DR& rcvr) noexcept : rcvr(rcvr) {}
@@ -7273,7 +7292,7 @@ namespace std::execution {
[[noreturn]] void return_void() noexcept { terminate(); }
coroutine_handle<> unhandled_stopped() noexcept {
- set_stopped((DR&&) rcvr);
+ set_stopped(std::move(rcvr));
return noop_coroutine();
}
@@ -7282,21 +7301,23 @@ namespace std::execution {
coroutine_handle<connect-awaitable-promise>::from_promise(*this)};
}
- env_of_t<const DR&> get_env() const noexcept {
+ env_of_t<DR> get_env() const noexcept {
return execution::get_env(rcvr);
}
+
+ private:
+ DR& rcvr; // exposition only
};
}
-4. Let `operation-state-task` be the following class:
+4. Let `operation-state-task` be the following exposition-only class:
namespace std::execution {
struct operation-state-task {
using operation_state_concept = operation_state_t;
using promise_type = connect-awaitable-promise;
- coroutine_handle<> coro; // exposition only
explicit operation-state-task(coroutine_handle<> h) noexcept : coro(h) {}
operation-state-task(operation-state-task&& o) noexcept
@@ -7306,6 +7327,9 @@ namespace std::execution {
void start() & noexcept {
coro.resume();
}
+
+ private:
+ coroutine_handle<> coro; // exposition only
};
}
@@ -7337,7 +7361,7 @@ namespace std::execution {
[[noreturn]] void await_resume() noexcept { unreachable(); }
};
return awaiter{fn};
- };
+ }
operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> {
exception_ptr ep;
@@ -7356,8 +7380,7 @@ namespace std::execution {
}
-6. If `Sndr` does not satisfy `sender` or if `Rcvr` does not satisfy `receiver`,
- `connect(sndr, rcvr)` is ill-formed. Otherwise, the expression `connect(sndr, rcvr)` is
+6. The expression `connect(sndr, rcvr)` is
expression-equivalent to:
1. `sndr.connect(rcvr)` if that expression is well-formed.
@@ -7365,27 +7388,27 @@ namespace std::execution {
* Mandates: The type of the expression above satisfies
`operation_state`.
- 2. Otherwise, connect-awaitable(sndr, rcvr)
if that expression is
- well-formed.
+ 2. Otherwise, connect-awaitable(sndr, rcvr)
.
+
+ 3. *Mandates:* `sender && receiver` is `true`.
- 3. Otherwise, `connect(sndr, rcvr)` is ill-formed.
### Sender factories [exec.factories] ### {#spec-execution.senders.factories}
#### `execution::schedule` [exec.schedule] #### {#spec-execution.senders.schedule}
-1. `schedule` obtains a schedule-sender ([async.ops]) from a scheduler.
+1. `schedule` obtains a schedule sender ([async.ops]) from a scheduler.
2. The name `schedule` denotes a customization point object. For a
- subexpression `sch`, the expression `schedule(sch)` is expression-equivalent to:
+ subexpression `sch`, the expression `schedule(sch)` is expression-equivalent to
+ `sch.schedule()`.
+
- 1. `sch.schedule()` if that expression is valid. If `sch.schedule()` does
- not return a sender whose `set_value` completion scheduler is equal
- to `sch`, the behavior of calling `schedule(sch)` is undefined.
+ 1. If the expression get_completion_scheduler<set_value_t>(
+ get_env(sch.schedule())) == sch
is ill-formed or evaluates
+ to `false`, the behavior of calling `schedule(sch)` is undefined.
- * Mandates: The type of `sch.schedule()` satisfies `sender`.
-
- 2. Otherwise, `schedule(sch)` is ill-formed.
+ 2. Mandates: The type of `sch.schedule()` satisfies `sender`.
#### `execution::just`, `execution::just_error`, `execution::just_stopped` [exec.just] #### {#spec-execution.senders.just}
@@ -7409,7 +7432,7 @@ namespace std::execution {
is `false`;
Otherwise, it is expression-equivalent to make-sender(just-cpo,
- product-type{vs...})
.
+ product-type{ts...}).
3. For `just`, `just_error`, and `just_stopped`, let `set-cpo`
be `set_value`, `set_error`, and `set_stopped` respectively. The
@@ -7459,33 +7482,34 @@ namespace std::execution {
#### General [exec.adapt.general] #### {#spec-execution.senders.adapt.general}
-1. Subclause [exec.adapt] specifies a set of sender adaptors.
+1. [exec.adapt] specifies a set of sender adaptors.
-2. The bitwise OR operator is overloaded for the purpose of creating sender
+2. The bitwise inclusive OR operator is overloaded for the purpose of creating sender
chains. The adaptors also support function call syntax with equivalent
semantics.
-3. Unless otherwise specified, 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. This requirement applies to any
- function that is selected by the implementation of the sender adaptor.
-
-4. Unless otherwise specified, a parent sender ([async.ops]) with a single child
- sender `sndr` has an associated attribute object equal to
- FWD-ENV(get_env(sndr))
([exec.fwd.env]). Unless
- otherwise specified, a parent sender with more than one child senders has an
- associated attributes object equal to empty_env{}
. These
- requirements apply to any function that is selected by the implementation of
- the sender adaptor.
-
-5. Unless otherwise specified, 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))
. This
- requirement applies to any sender returned from a function that is selected
- by the implementation of such sender adaptor.
-
-6. If a sender returned from a sender adaptor specified in this subclause is
+3. Unless otherwise specified:
+
+ 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.
+
+ 2. A parent sender ([async.ops]) with a single child
+ sender `sndr` has an associated attribute object equal to
+ FWD-ENV(get_env(sndr))
([exec.fwd.env]).
+
+ 3. A parent sender with more than one child sender has an
+ associated attributes object equal to empty_env{}
.
+
+ 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))
.
+
+ These requirement apply to any function that is selected by the implementation
+ of the sender adaptor.
+
+4. 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` denotes the type `exception_ptr`, but the implementation
does not potentially evaluate an error completion operation with an
@@ -7495,7 +7519,7 @@ namespace std::execution {
#### Sender adaptor closure objects [exec.adapt.objects] #### {#spec-execution.senders.adaptor.objects}
1. A pipeable sender adaptor closure object is a function object that
- accepts one or more `sender` arguments and returns a `sender`. For a sender
+ accepts one or more `sender` arguments and returns a `sender`. For a pipeable sender
adaptor closure object `c` and an expression `sndr` such that
`decltype((sndr))` models `sender`, the following expressions are equivalent
and yield a `sender`:
@@ -7512,22 +7536,22 @@ namespace std::execution {
`e` is a perfect forwarding call wrapper ([func.require]) with the following
properties:
- - Its target object is an object `d2` of type `decay_t`
+ - Its target object is an object `d2` of type `decltype(auto(d))`
direct-non-list-initialized with `d`.
- It has one bound argument entity, an object `c2` of type
- `decay_t` direct-non-list-initialized with `C`.
+ `decltype(auto(c))` direct-non-list-initialized with `c`.
- Its call pattern is `d2(c2(arg))`, where `arg` is the argument used in a
function call expression of `e`.
The expression `c | d` is well-formed if and only if the initializations of
- the state entities of `e` are all well-formed.
+ the state entities ([func.def]) of `e` are all well-formed.
2. An object `t` of type `T` is a pipeable sender adaptor closure object if `T`
models `derived_from>`, `T` has no other base
classes of type `sender_adaptor_closure` for any other type `U`, and `T`
- does not model `sender`.
+ does not satisfy `sender`.
3. The template parameter `D` for `sender_adaptor_closure` can be an incomplete
type. Before any expression of type cv D
appears as an
@@ -7546,11 +7570,10 @@ namespace std::execution {
6. If a pipeable sender adaptor object `adaptor` accepts more than one argument,
then let `sndr` be an expression such that `decltype((sndr))` models
`sender`, let `args...` be arguments such that `adaptor(sndr, args...)` is a
- well-formed expression as specified in the rest of this subclause
- ([exec.adapt.objects]), and let `BoundArgs` be a pack that denotes
- `decay_t...`. The expression `adaptor(args...)` produces a
- pipeable sender adaptor closure object `f` that is a perfect forwarding call
- wrapper with the following properties:
+ well-formed expression as specified below, and let `BoundArgs` be a pack
+ that denotes `decltype(auto(args))...`. The expression `adaptor(args...)`
+ produces a pipeable sender adaptor closure object `f` that is a perfect
+ forwarding call wrapper with the following properties:
- Its target object is a copy of `adaptor`.
@@ -7570,7 +7593,7 @@ namespace std::execution {
1. `on` adapts an input sender into a sender that will start on an execution
agent belonging to a particular scheduler's associated execution resource.
-2. The name `on` denotes a customization point object. For some subexpressions
+2. The name `on` denotes a customization point object. For subexpressions
`sch` and `sndr`, if `decltype((sch))` does not satisfy `scheduler`, or
`decltype((sndr))` does not satisfy `sender`, `on(sch, sndr)` is ill-formed.
@@ -7579,9 +7602,11 @@ namespace std::execution {
transform_sender(
query-or-default(get_domain, sch, default_domain()),
- make-sender(on, sch, sndr));
+ make-sender(on, sch, sndr))
+ except that `sch` is evaluated only once.
+
4. Let `out_sndr` and `env` be subexpressions such that `OutSndr` is `decltype((out_sndr))`. If
sender-for<OutSndr, on_t>
is `false`, then the expressions
`on.transform_env(out_sndr, env)` and `on.transform_sender(out_sndr, env)` are ill-formed;
@@ -7590,17 +7615,18 @@ namespace std::execution {
- `on.transform_env(out_sndr, env)` is equivalent to:
- auto&& [ign1, sch, ign2] = out_sndr;
+ auto&& [_, sch, _] = out_sndr;
return JOIN-ENV(SCHED-ENV(sch), FWD-ENV(env));
- `on.transform_sender(out_sndr, env)` is equivalent to:
- auto&& [ign, sch, sndr] = out_sndr;
+ auto&& [_, sch, sndr] = out_sndr;
return let_value(
schedule(sch),
- [sndr = std::forward_like<OutSndr>(sndr)]() mutable {
+ [sndr = std::forward_like<OutSndr>(sndr)]() mutable
+ noexcept(is_nothrow_move_constructible_v) {
return std::move(sndr);
});
@@ -7611,16 +7637,14 @@ namespace std::execution {
type `Env` such that `sender_in` is `true`. Let `op` be an lvalue
referring to the operation state that results from connecting `out_sndr` with
`out_rcvr`. Calling `start(op)` shall start `sndr` on an execution agent of the
- associated execution resource of `sch`, or failing that, shall execute an
- error completion on `out_rcvr`.
+ associated execution resource of `sch`. If scheduling onto `sch` fails, an error
+ completion on `out_rcvr` shall be executed on an unspecified execution agent.
#### `execution::transfer` [exec.transfer] #### {#spec-execution.senders.adapt.transfer}
-1. `transfer` adapts a sender into one with a different associated `set_value`
- completion scheduler. It results in a transition
- between different execution resources when executed.
+1. `transfer` adapts a sender into one that completes on the specified scheduler.
-2. The name `transfer` denotes a customization point object. For some
+2. The name `transfer` denotes a customization point object. For
subexpressions `sch` and `sndr`, if `decltype((sch))` does not satisfy
`scheduler`, or `decltype((sndr))` does not satisfy `sender`,
`transfer(sndr, sch)` is ill-formed.
@@ -7630,9 +7654,11 @@ namespace std::execution {
transform_sender(
get-domain-early(sndr),
- make-sender(transfer, sch, sndr));
+ make-sender(transfer, sch, sndr))
+ except that `sndr` is evaluated only once.
+
4. The exposition-only class template `impls-for` is specialized
for `transfer_t` as follows:
@@ -7654,13 +7680,13 @@ namespace std::execution {
is equal to:
- auto [tag, data, child] = sndr;
+ auto [_, data, child] = sndr;
return schedule_from(std::move(data), std::move(child));
This causes the `transfer(sndr, sch)` sender to become
- `schedule_from(sch, sndr)` when it is connected with a receiver with an
- execution domain that does not customize `transfer`.
+ `schedule_from(sch, sndr)` when it is connected with a receiver whose
+ execution domain does not customize `transfer`.
6. Let `out_sndr` be a subexpression denoting a sender returned from
`transfer(sndr, sch)` or one equal to such, and let `OutSndr` be the type
@@ -7670,8 +7696,8 @@ namespace std::execution {
results from connecting `out_sndr` with `out_rcvr`. 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`. If scheduling onto `sch` fails, execute an error
- completion on `out_rcvr` on an unspecified execution agent.
+ associated with `sch`. If scheduling onto `sch` fails, an error completion
+ on `out_rcvr` shall be executed on an unspecified execution agent.
#### `execution::schedule_from` [exec.schedule.from] #### {#spec-execution.senders.adaptors.schedule_from}
@@ -7683,7 +7709,7 @@ namespace std::execution {
2. The name `schedule_from` denotes a customization point object. For some
subexpressions `sch` and `sndr`, let `Sch` be `decltype((sch))` and `Sndr` be
`decltype((sndr))`. If `Sch` does not satisfy `scheduler`, or `Sndr` does not
- satisfy `sender`, `schedule_from` is ill-formed.
+ satisfy `sender`, `schedule_from(sch, sndr)` is ill-formed.
3. Otherwise, the expression `schedule_from(sch, sndr)` is expression-equivalent
to:
@@ -7691,9 +7717,11 @@ namespace std::execution {
transform_sender(
query-or-default(get_domain, sch, default_domain()),
- make-sender(schedule_from, sch, sndr));
+ make-sender(schedule_from, sch, sndr))
+ except that `sch` is evaluated only once.
+
4. The exposition-only class template `impls-for`
([exec.snd.general]) is specialized for `schedule_from_t` as
follows:
@@ -7722,30 +7750,32 @@ namespace std::execution {
with a callable object equivalent to the following lambda:
- []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr)
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below)
requires sender_in<child-type<Sndr>, env_of_t<Rcvr>> {
- return apply(
- [&]<class Sch, class Child>(auto, Sch sch, Child&& child) {
- using variant-type = see below;
- using receiver-type = see below;
- using operation-type = connect_result_t<schedule_result_t<Sch>, receiver-type>;
-
- struct state-type {
- Rcvr& rcvr;
- variant-type async-result;
- operation-type op-state;
-
- explicit state-type(Sch sch, Rcvr& rcvr)
- : rcvr(rcvr), op-state(connect(schedule(sch), receiver-type{{}, this})) {}
- };
- return state-type{sch, rcvr};
- },
- std::forward<Sndr>(sndr));
+ auto& [_, sch, child] = sndr;
+
+ using sched-t = decltype(auto(sch));
+ using variant-type = see below;
+ using receiver-type = see below;
+ using operation-type = connect_result_t<schedule_result_t<sched-t>, receiver-type>;
+ constexpr bool nothrow = noexcept(connect(schedule(sch), receiver-type{nullptr}));
+
+ struct state-type {
+ Rcvr& rcvr;
+ variant-type async-result;
+ operation-type op-state;
+
+ explicit state-type(sched-t sch, Rcvr& rcvr) noexcept(nothrow)
+ : rcvr(rcvr), op-state(connect(schedule(sch), receiver-type{this})) {}
+ };
+
+ return state-type{sch, rcvr};
}
- 1. The local class `state-type` is a structural type.
+ 1. Objects of the local class `state-type` can be used to
+ initialize a structured binding.
2. Let `Sigs` be a pack of the arguments to the
`completion_signatures` specialization named by
@@ -7766,9 +7796,6 @@ namespace std::execution {
using receiver_concept = receiver_t;
state-type* state; // exposition only
- Rcvr&& base() && noexcept { return std::move(state->rcvr); }
- const Rcvr& base() const & noexcept { return state->rcvr; }
-
void set_value() && noexcept {
visit(
[this]<class Tuple>(Tuple& result) noexcept -> void {
@@ -7796,6 +7823,10 @@ namespace std::execution {
}
+ 4. 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`.
+
3. The member impls-for<schedule_from_t>::complete
is initialized with a callable object equivalent to the following lambda:
@@ -7817,18 +7848,16 @@ namespace std::execution {
};
-5. Let the subexpression `out_sndr` denote the result of the invocation
- `schedule_from(sch, sndr)` or an object copied or moved from such, and let
- the subexpression `rcvr` denote a receiver such that the expression
- `connect(out_sndr, rcvr)` is well-formed. The expression
- `connect(out_sndr, rcvr)` has undefined behavior unless it creates an
- asynchronous operation ([async.ops]) that, when started:
-
- - eventually completes on an execution agent belonging to the associated
- execution resource of `sch`, and
-
- - completes with the same async result as `sndr`.
-
+5. 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))`. Let `out_rcvr` be a subexpression denoting a
+ receiver that has an environment of type `Env` such that `sender_in` is `true`. Let `op` be an lvalue referring to the operation state that
+ results from connecting `out_sndr` with `out_rcvr`. 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`. If scheduling onto `sch` fails, an error completion
+ on `out_rcvr` shall be executed on an unspecified execution agent.
#### `execution::then`, `execution::upon_error`, `execution::upon_stopped` [exec.then] #### {#spec-execution.senders.adaptor.then}
@@ -7839,9 +7868,9 @@ namespace std::execution {
2. The names `then`, `upon_error`, and `upon_stopped` denote customization point
objects. Let the expression `then-cpo` be one of `then`,
- `upon_error`, or `upon_stopped`. For subexpressions `sndr` and `f`, let `Sndr` be
- `decltype((sndr))` and let `F` be the decayed type of `f`. If `Sndr` does not
- satisfy `sender`, or `F` does not satisfy `movable-value`,
+ `upon_error`, or `upon_stopped`. For subexpressions `sndr` and `f`, if
+ `decltype(sndr)` does not satisfy `sender`, or `decltype(f)` does not
+ satisfy `movable-value`,
then-cpo(sndr, f)
is ill-formed.
3. Otherwise, the expression then-cpo(sndr, f)
is
@@ -7850,8 +7879,10 @@ namespace std::execution {
transform_sender(
get-domain-early(sndr),
- make-sender(then-cpo, f, sndr));
+ make-sender(then-cpo, f, sndr))
+
+ except that `sndr` is evaluated only once.
4. For `then`, `upon_error`, and `upon_stopped`, let `set-cpo`
be `set_value`, `set_error`, and `set_stopped` respectively. The
@@ -7864,7 +7895,7 @@ namespace std::execution {
struct impls-for<decayed-typeof<then-cpo>> : default-impls {
static constexpr auto complete =
[]<class Tag, class... Args>
- (auto /*index*/, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {
+ (auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {
if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
TRY-SET-VALUE(std::move(rcvr),
invoke(std::move(fn), std::forward<Args>(args)...));
@@ -7880,8 +7911,8 @@ namespace std::execution {
unless it returns a sender `out_sndr` that:
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
+ datums of `sndr` for `then`, `upon_error`, and `upon_stopped`
+ respectively, using the result value of `f` as `out_sndr`'s value
completion, and
2. Forwards all other completion operations unchanged.
@@ -7893,10 +7924,10 @@ namespace std::execution {
operation by passing the sender's result datums to a user-specified
callable, which returns a new sender that is connected and started.
-2. Let the expression `let-cpo` be one of `let_value`,
- `let_error`, or `let_stopped` and let `set-cpo` be the
- completion function that corresponds to `let-cpo`
- (`set_value` for `let_value`, etc.). For a subexpression `sndr`, let
+2. For `let_value`, `let_error`, and `let_stopped`, let `set-cpo`
+ be `set_value`, `set_error`, and `set_stopped` respectively.
+ Let the expression `let-cpo` be one of `let_value`,
+ `let_error`, or `let_stopped`. For a subexpression `sndr`, let
let-env(sndr)
be expression-equivalent to the first
well-formed expression below:
@@ -7904,12 +7935,12 @@ namespace std::execution {
- MAKE-ENV(get_domain, get_domain(get_env(sndr)))
- - `empty_env{}`
+ - `(void(sndr), empty_env{})`
3. The names `let_value`, `let_error`, and `let_stopped` denote customization
- point objects. For subexpressions `sndr` and `f`, let `Sndr` be `decltype((sndr))`,
- let `F` be the decayed type of `f`. If `Sndr` does not satisfy `sender` or if `F`
- does not satisfy `movable-value`, the expression
+ point objects. For subexpressions `sndr` and `f`, let `F` be the decayed
+ type of `f`. If `decltype(sndr)` does not satisfy `sender` or if
+ `decltype(f)` does not satisfy `movable-value`, the expression
let-cpo(sndr, f)
is ill-formed. If `F` does not satisfy
`invocable`, the expression `let_stopped(sndr, f)` is ill-formed.
@@ -7919,9 +7950,11 @@ namespace std::execution {
transform_sender(
get-domain-early(sndr),
- make-sender(let-cpo, f, sndr));
+ make-sender(let-cpo, f, sndr))
+ except that `sndr` is evaluated only once.
+
5. The exposition-only class template `impls-for`
([exec.snd.general]) is specialized for `let-cpo` as
follows:
@@ -7944,38 +7977,51 @@ namespace std::execution {
namespace std::execution {
template<class Rcvr, class Env>
- struct receiver2 : Rcvr {
- explicit receiver2(Rcvr rcvr, Env env)
- : Rcvr(std::move(rcvr)), env(std::move(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));
+ }
- auto get_env() const noexcept {
- const Rcvr& rcvr = *this;
+ decltype(auto) get_env() const noexcept {
return JOIN-ENV(env, FWD-ENV(execution::get_env(rcvr)));
}
+ Rcvr& rcvr; // exposition only
Env env; // exposition only
};
}
2. impls-for<decayed-typeof<let-cpo>>::get-state
is
- is initialized with a callable object equivalent to the following:
+ initialized with a callable object equivalent to the following:
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires see below {
- auto&& [tag, data, child] = std::forward<Sndr>(sndr);
- return [&]<class Fn, class Env>(Fn fn, Env env) {
- using args-variant-type = see below;
- using ops2-variant-type = see below;
-
- struct state-type {
- Fn fn;
- Env env;
- args-variant-type args;
- ops2-variant-type ops2;
- };
- return state-type{std::move(fn), std::move(env), {}, {}};
- }(std::forward_like<Sndr>(data), let-env(child));
+ auto& [_, fn, child] = sndr;
+ using fn-t = decay_t<decltype(fn)>;
+ using env-t = decltype(let-env(child));
+ using args-variant-type = see below;
+ using ops2-variant-type = see below;
+
+ struct state-type {
+ Fn fn;
+ Env env;
+ args-variant-type args;
+ ops2-variant-type ops2;
+ };
+ return state-type{std::forward_like<Sndr>(fn), let-env(child), {}, {}};
}
@@ -7988,7 +8034,7 @@ namespace std::execution {
as-tuple<Tag(Args...)>
denotes the type
decayed-tuple<Args...>
. Then
`args-variant-type` denotes the type variant<monostate,
- as-tuple<LetSigs>...>
.
+ as-tuple<LetSigs>...> except with duplicate types removed.
2. Let `as-sndr2` be an alias template such that
as-sndr2<Tag(Args...)>
denotes the type
@@ -7996,21 +8042,22 @@ namespace std::execution {
Then `ops2-variant-type` denotes the type
variant<monostate,
connect_result_t<as-sndr2<LetSigs>,
- receiver2<Rcvr, Env>>...>
.
+ receiver2<Rcvr, Env>>...> except with duplicate types removed.
3. The requires-clause constraining the above lambda is
satisfied if and only if the types `args-variant-type`
and `ops2-variant-type` are well-formed.
- 3. The exposition-only function template `let-bind` is equal to:
+ 3. The exposition-only function template `let-bind` has effects equivalent to:
- auto& args = state.args.emplace<decayed-tuple<Args...>>(std::forward<Args>(args)...);
- auto sndr2 = apply(std::move(state.fn), args);
- auto rcvr2 = receiver2{std::move(rcvr), std::move(state.env)};
- auto mkop2 = [&] { return connect(std::move(sndr2), std::move(rcvr2)); };
- auto& op2 = state.ops2.emplace<decltype(mkop2())>(emplace-from{mkop2});
- start(op2);
+ using args_t = decayed-tuple<Args...>;
+ auto mkop2 = [&] {
+ return connect(
+ apply(std::move(state.fn), state.args.emplace<args_t>(std::forward<Args>(args)...)),
+ receiver2{rcvr, std::move(state.env)});
+ };
+ start(state.ops2.emplace<decltype(mkop2())>(emplace-from{mkop2}));
4. impls-for<decayed-typeof<let-cpo>>::complete
is
@@ -8034,7 +8081,7 @@ namespace std::execution {
JOIN-ENV(let-env(sndr), FWD-ENV(env))
.
7. Let the subexpression `out_sndr` denote the result of the invocation
- let-cpo(sndr, f)
or an object copied or moved from such,
+ 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. The expression `connect(out_sndr,
rcvr)` has undefined behavior unless it creates an asynchronous operation
@@ -8053,21 +8100,22 @@ namespace std::execution {
1. `bulk` runs a task repeatedly for every index in an index space.
2. The name `bulk` denotes a customization point object. For subexpressions
- `sndr`, `shape`, and `f`, let `Sndr` be `decltype((sndr))`, let `Shape` be
- the decayed type of `shape`, and let `F` be the decayed type of `f`. If
- `Sndr` does not satisfy `sender`, or if `Shape` does not satisfy `integral`,
- or if `F` does not satisfy `movable-value`, bulk(sndr,
- shape, f)
is ill-formed.
+ `sndr`, `shape`, and `f`, let `Shape` be `decltype(auto(shape))`. If
+ `decltype((sndr))` does not satisfy `sender`, or if `Shape` does not
+ satisfy `integral`, or if `decltype((f))` does not satisfy `movable-value`,
+ `bulk(sndr, shape, f)` is ill-formed.
-3. Otherwise, the expression bulk(sndr, shape, f)
is
+3. Otherwise, the expression `bulk(sndr, shape, f)` is
expression-equivalent to:
transform_sender(
get-domain-early(sndr),
- make-sender(bulk, product-type{shape, f}, sndr));
+ make-sender(bulk, product-type{shape, f}, sndr))
+ except that `sndr` is evaluated only once.
+
4. The exposition-only class template `impls-for`
([exec.snd.general]) is specialized for `bulk_t` as follows:
@@ -8090,7 +8138,7 @@ namespace std::execution {
auto& [shape, f] = state;
constexpr bool nothrow = noexcept(f(auto(shape), args...));
TRY-EVAL(std::move(rcvr), [&]() noexcept(nothrow) {
- for (auto max = shape, i = 0; i < max; ++i) {
+ for (decltype(auto(shape)) i = 0; i < shape; ++i) {
f(auto(i), args...);
}
Tag()(std::move(rcvr), std::forward<Args>(args)...);
@@ -8106,7 +8154,7 @@ namespace std::execution {
or if the expression `f(auto(shape), args...)` is well-formed.
5. Let the subexpression `out_sndr` denote the result of the invocation
- bulk(sndr, shape, f)
or an object copied or moved from such,
+ `bulk(sndr, 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. The expression `connect(out_sndr,
rcvr)` has undefined behavior unless it creates an asynchronous operation
@@ -8119,6 +8167,8 @@ namespace std::execution {
- propagates all completion operations sent by `sndr`.
+
+
#### `execution::split` and `execution::ensure_started` [exec.split] #### {#spec-execution.senders.adapt.split}
1. `split` adapts an arbitrary sender into a sender that can be connected