[exec.associate] # 33 Execution control library [[exec]](./#exec) ## 33.9 Senders [[exec.snd]](exec.snd#exec.associate) ### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.associate) #### 33.9.12.16 execution​::​associate [exec.associate] [1](#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[.](#1.sentence-1) [2](#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.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>struct *associate-data* { // *exposition only*using *wrap-sender* = // *exposition only* remove_cvref_t().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> 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.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>*associate-data*(Token, Sender&&) -> *associate-data*;} [3](#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[.](#3.sentence-1) [🔗](#lib:execution::associate-data,constructor) `associate-data(const associate-data& other) noexcept(is_nothrow_copy_constructible_v && noexcept(other.token.try_associate())); ` [4](#4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4957) *Constraints*: [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]")<*wrap-sender*> is true[.](#4.sentence-1) [5](#5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4961) *Effects*: Value-initializes *sndr* and initializes *token* with other.*token*[.](#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[.](#5.sentence-2) [🔗](#lib:execution::associate-data,constructor_) `associate-data(associate-data&& other) noexcept(is_nothrow_move_constructible_v); ` [6](#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()[.](#6.sentence-1) [🔗](#lib:execution::associate-data,destructor) `~associate-data(); ` [7](#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()[.](#7.sentence-1) [🔗](#lib:release,execution::associate-data) `optional> release() && noexcept(is_nothrow_move_constructible_v); ` [8](#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 as if by:return optional(pair(*token*, std::move(**sndr*))); [9](#9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5020) *Postconditions*: *sndr* does not contain a value[.](#9.sentence-1) [10](#10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5025) The name associate denotes a pipeable sender adaptor object[.](#10.sentence-1) For subexpressions sndr and token: - [(10.1)](#10.1) If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), orremove_cvref_t does not satisfy [scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]"), thenassociate(sndr, token) is ill-formed. - [(10.2)](#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](#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.2 Exposition-only entities")) is specialized for associate_t as follows: [🔗](#lib:execution::impls-for%3cassociate_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *get-state* = *see below*; // *exposition only*static constexpr auto *start* = *see below*; // *exposition only*templatestatic consteval void *check-types*() { // *exposition only*using associate_data_t = remove_cvref_t<*data-type*>; using child_type_t = typename associate_data_t::*wrap-sender*; (void)get_completion_signatures(); }};} [12](#12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5066) The member *impls-for*​::​*get-state* is initialized with a callable object equivalent to the following lambda: [](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; 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](#13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5126) The expression in the noexcept clause of*impls-for*​::​*get-state* isis_nothrow_constructible_v, Sndr> && is_nothrow_move_constructible_v<*wrap-sender*> &&[*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header synopsis [functional.syn]") where *wrap-sender* is the typeremove_cvref_t<*data-type*>​::​*wrap-sender*[.](#13.sentence-1) [14](#14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5137) The member *impls-for*​::​*start* is initialized with a callable object equivalent to the following lambda:[](auto& state, auto&) noexcept -> void { state.*run*();} [15](#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[.](#15.sentence-1)