std::execution::then
| Defined in header <execution>
|
||
struct then_t { /*unspecified*/ };
|
(1) | (since C++26) |
inline constexpr then_t then{};
|
(2) | (since C++26) (customization point object) |
| Call signature |
||
execution::sender auto then( execution::sender auto sndr,
/* movable value */ auto f );
|
(since C++26) | |
Attaches a function to a sender: when sndr completes with values, f is called with them, and the result of the call becomes the value the returned sender completes with.
then is the basic way to do something with the result of asynchronous work, and the building block of most sender pipelines. It is the sender counterpart of calling a function on a result you already have.
then is a pipeable sender adaptor: sndr | then(f) is equivalent to then(sndr, f), which makes chains read in the order the work happens.
Parameters
| sndr | - | the sender whose values are passed to f
|
| f | - | the function (or other callable) to call with the values sent by sndr
|
Return value
A sender that behaves like sndr, except that each value completion of sndr is replaced by a call of f with the values it carries:
- The result of
fis sent as the value of the returned sender. Iffreturnsvoid, the returned sender completes with no values. - If
fthrows, the returned sender completes with the exception as an error (set_errorwith a std::exception_ptr).
Error and stopped completions of sndr are forwarded unchanged: f is not called for them. Use upon_error and upon_stopped to handle those.
then(sndr, f) is ill-formed if sndr does not satisfy execution::sender or f cannot be copied or moved into the sender (it must be a movable value, and not an array). It is also diagnosed at compile time if f cannot be called with the values of some value completion of sndr.
Customization point objects
The name execution::then denotes a customization point object, which is a const function object of a literal semiregular class type. The cv-unqualified version of its type is a tag type denoted as execution::then_t. See CustomizationPointObject for details.
Details
- Nothing runs when the sender is built.
fis called only after the returned sender has been connected to a receiver and started, and only if and whensndrcompletes with values. Building a chain ofthencalls only describes the work. fruns on the execution agent on whichsndrcompletes, immediately in the completion.thennever moves work to another thread; to change where the next step runs, use continues_on or on.- If
sndrcan complete with several different sets of values,fmust be callable with each of them (a generic lambda works).
Notes
- The result of
fis a plain value. Iffreturns a sender, that sender is sent as a value, it is not started and not waited for. To continue with further asynchronous work depending on a result, use let_value. thenonly handles the value channel. upon_error and upon_stopped do the same for the error and stopped channels; likethen, they send the result of the function as a value, which turns a failure into a normal result.- Exceptions thrown by
fbecome an error completion of the returned sender, so they are reported through the same channel as errors fromsndr. Whoever consumes the sender, for example this_thread::sync_wait, decides what happens to them.
Example
then turns the two values sent by just into one result, and then into text. Nothing runs until sync_wait starts the sender. The standard header is not available in the online compilers yet, so this link runs the example on Compiler Explorer with stdexec, the reference implementation.
#include <execution>
#include <iostream>
#include <string>
#include <thread>
namespace ex = std::execution;
int main()
{
auto sndr = ex::just(6, 7)
| ex::then([](int a, int b)
{
std::cout << "multiplying\n";
return a * b;
})
| ex::then([](int product)
{
return "answer: " + std::to_string(product);
});
std::cout << "sender built, nothing has run yet\n";
auto [text] = std::this_thread::sync_wait(std::move(sndr)).value();
std::cout << text << '\n';
}
Output:
sender built, nothing has run yet
multiplying
answer: 42
An exception thrown by a function becomes an error. The next then is skipped, and upon_error turns the error into a value. The standard header is not available in the online compilers yet, so this link runs the example on Compiler Explorer with stdexec, the reference implementation.
#include <exception>
#include <execution>
#include <iostream>
#include <stdexcept>
#include <thread>
namespace ex = std::execution;
int main()
{
auto sndr = ex::just(0)
| ex::then([](int divisor)
{
if (divisor == 0)
throw std::runtime_error("division by zero");
return 10 / divisor;
})
| ex::then([](int quotient)
{
std::cout << "not reached\n";
return quotient;
})
| ex::upon_error([](std::exception_ptr)
{
return -1;
});
auto [result] = std::this_thread::sync_wait(std::move(sndr)).value();
std::cout << "result: " << result << '\n';
}
Output:
result: -1
See also
(C++26) |
chains a function onto a sender, calling it with the error if the sender fails and sending its result on as a value (customization point object) |
(C++26) |
chains a function onto a sender, calling it if the sender is cancelled and sending its result on as a value (customization point object) |
(C++26) |
chains a function onto a sender, calling it with the values the sender sends and continuing with the sender it returns (customization point object) |