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

8.9 KiB
Raw Blame History

[exec.associate]

33 Execution control library [exec]

33.9 Senders [exec.snd]

33.9.12 Sender adaptors [exec.adapt]

33.9.12.16 execution::associate [exec.associate]

1

#

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.

2

#

Let associate-data be the following exposition-only class template:

🔗

namespace std::execution {template<scope_token Token, sender Sender>struct associate-data { // exposition onlyusing wrap-sender = // exposition only remove_cvref_t<decltype(declval<Token&>().wrap(declval()))>; explicit associate-data(Token t, Sender&& s): sndr(t.wrap(std::forward(s))), token(t) {if (!token.try_associate())sndr.reset(); }associate-data(const associate-data& other)noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&noexcept(other.token.try_associate())); associate-data(associate-data&& other)noexcept(is_nothrow_move_constructible_v<wrap-sender>); ~associate-data();

optional<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 Token, sender Sender>associate-data(Token, Sender&&) -> associate-data<Token, Sender>;}

3

#

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.

🔗

associate-data(const associate-data& other) noexcept(is_nothrow_copy_constructible_v<wrap-sender> && noexcept(other.token.try_associate()));

4

#

Constraints: copy_constructible<wrap-sender> is true.

5

#

Effects: Value-initializes sndr and initializes token with other.token.

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.

🔗

associate-data(associate-data&& other) noexcept(is_nothrow_move_constructible_v<wrap-sender>);

6

#

Effects: Initializes sndr with std::move(other.sndr) and initializes token with std::move(other.token) and then calls other.sndr.reset().

🔗

~associate-data();

7

#

Effects: If sndr.has_value() returns false then no effect; otherwise, invokes sndr.reset() before invoking token.disassociate().

🔗

optional<pair<Token, wrap-sender>> release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);

8

#

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

#

Postconditions: sndr does not contain a value.

10

#

The name associate denotes a pipeable sender adaptor object.

For subexpressions sndr and token:

If decltype((sndr)) does not satisfy sender, orremove_cvref_t<decltype((token))> does not satisfy scope_token, thenassociate(sndr, token) is ill-formed.

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

#

The exposition-only class template impls-for ([exec.snd.expos]) is specialized for associate_t as follows:

🔗

namespace std::execution {template<>struct impls-for<associate_t> : default-impls {static constexpr auto get-state = see below; // exposition onlystatic constexpr auto start = see below; // exposition onlytemplate<class Sndr, class... Env>static consteval void check-types() { // exposition onlyusing associate_data_t = remove_cvref_t<data-type>; using child_type_t = typename associate_data_t::wrap-sender; (void)get_completion_signatures<child_type_t, FWD-ENV-T(Env)...>(); }};}

12

#

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); 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 onlyunion { Rcvr* rcvr; // exposition onlystruct { 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 onlyif (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

#

The expression in the noexcept clause ofimpls-for<associate_t>::get-state isis_nothrow_constructible_v<remove_cvref_t, Sndr> && is_nothrow_move_constructible_v<wrap-sender> &&nothrow-callable<connect_t, wrap-sender, Rcvr> where wrap-sender is the typeremove_cvref_t<data-type>::wrap-sender.

14

#

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

#

The evaluation of associate(sndr, token) may cause side effects observable via token's associated async scope object.