#
when_all and when_all_with_variant both adapt multiple input senders into a sender that completes when all input senders have completed.
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.
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.
2
#
The names when_all and when_all_with_variant denote customization point objects.
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)).>.
Let CD2 be CD if CD is well-formed, and default_domain otherwise.
The expressions when_all(sndrs..) and when_all_with_variant(sndrs..) are ill-formed if any of the following is true:
The expression when_all(sndrs..) is expression-equivalent to: transform_sender(CD2(), make-sender(when_all, {}, sndrs..)
The exposition-only class template impls-for ([exec.snd.expos]) is specialized for when_all_t as follows: 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(); }; }
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;
Returns: An object e such that
  • decltype(e) models queryable, and
  • e.query(get_stop_token) is expression-equivalent to stop_src.get_token(), and
  • given a query object q with type other than cv get_stop_token_t, e.query(q) is expression-equivalent to env.query(q) if the type of q satisfies forwarding-query, and ill-formed otherwise.
Let when-all-env be an alias template such that when-all-env<Env> denotes the type decltype(make-
when-all-env
(declval<inplace_stop_source&>(), declval<Env>()
.
template<class Sndr, class. Env> static consteval void check-types();
Let Is be the pack of integral template arguments of the integer_sequence specialization denoted by indices-for<Sndr>.
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] }; (fn.template operator()<child-type<Sndr, Is>>(), ..);
Throws: Any exception thrown as a result of evaluating the Effects, or an exception of type unspecified-​exception ([exec.snd.general]) when CD is ill-formed.
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<CD, default_domain>) { return env<>(); } else { return MAKE-ENV(get_domain, CD(); } }
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)); }
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 expression std::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{}; } };
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.
The alias values_tuple denotes the type tuple<value_types_of_t<Sndrs, FWD-ENV-T(env_of_t<Rcvr>), decayed-tuple, optional>.> if that type is well-formed; otherwise, tuple<>.
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.
The member void state-type​::​complete(Rcvr& rcvr) noexcept behaves as follows:
  • 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);
  • Otherwise, if disp is equal to disposition​::​error, evaluates: on_stop.reset(); visit( [&]<class Error>(Error& error) noexcept { if constexpr (!same_as<Error, none-such>) { set_error(std::move(rcvr), std::move(error)); } }, errors);
  • Otherwise, evaluates: if constexpr (sends-stopped) { on_stop.reset(); set_stopped(std::move(rcvr)); } where sends-stopped equals true if and only if there exists an element S of Sndrs such that completion_signatures_of_t<S, when-all-env<Env>> contains set_stopped_t().
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}); (start(ops), ..); }
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<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<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<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..).
The expression when_all_with_variant(sndrs..) is expression-equivalent to: transform_sender(CD2(), make-sender(when_all_with_variant, {}, sndrs..);
Given subexpressions sndr and env, if sender-for<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: 
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.
— end note]

Follow Lee on X/Twitter - Father, Husband, Serial builder creating AI, crypto, games & web tools. We are friends :) AI Will Come To Life!

Check out: eBank.nz (Art Generator) | Netwrck.com (AI Tools) | Text-Generator.io (AI API) | BitBank.nz (Crypto AI) | ReadingTime (Kids Reading) | RewordGame | BigMultiplayerChess | WebFiddle | How.nz | Helix AI Assistant