33 Execution control library [exec]

33.9 Senders [exec.snd]

33.9.12 Sender adaptors [exec.adapt]

33.9.12.7 execution​::​then, execution​::​upon_error, execution​::​upon_stopped [exec.then]

then attaches an invocable as a continuation for an input sender's value completion operation.
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.
The names then, upon_error, and upon_stopped denote pipeable sender adaptor objects.
Let the expression then-cpo be one of then, 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.
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.
For then, upon_error, and upon_stopped, let set-cpo be set_value, set_error, and set_stopped, respectively.
The exposition-only class template impls-for ([exec.snd.general]) is specialized for then-cpo as follows: 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<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)...); } }; }; }
The expression then-cpo(sndr, f) has undefined behavior unless it returns a senderout_sndr that
  • 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
  • forwards all other completion operations unchanged.