future/promise and callbacks: composition means blocking .get() or nesting lambdas — no continuations, no cancellation.
auto f = std::async(std::launch::async, fetch, id);
auto v = f.get(); // blocks: composition by waiting
From futures to senders/receivers and structured concurrency.
future/promise and callbacks: composition means blocking .get() or nesting lambdas — no continuations, no cancellation.
auto f = std::async(std::launch::async, fetch, id);
auto v = f.get(); // blocks: composition by waiting
shared_future enables fan-out; the Concurrency TS experiments with .then continuations that never reach the standard.
Status quo; real systems compose on library executors (Asio, folly) while the committee's executor designs churn.
Coroutines reframe composition as sequential-looking code, and stop_token gives cancellation a standard spelling — but scheduling and task types remain per-library (Asio awaitables, cppcoro, libunifex).
awaitable<Reply> round_trip(Conn& c, Request r) {
co_await c.send(r); // Asio-style, pre-std
co_return co_await c.receive();
}
std::generator covers synchronous sequences only; asynchronous composition is explicitly deferred to the senders/receivers work.
std::execution (P2300): senders describe work, receivers consume it, schedulers place it. Lazy, composable, cancellation-aware — “ranges for async” — with then, when_all, sync_wait, scopes for structured lifetimes, and a coroutine task on top.
using namespace std::execution;
auto work = schedule(pool.get_scheduler())
| then([] { return load(); })
| then([](Data d) { return transform(d); });
auto [result] = std::this_thread::sync_wait(std::move(work)).value();
Direction: async I/O and networking rebuilt on senders, async cleanup, and a wider algorithm set (retry, timeouts) — the declared roadmap after P2300.
C++26 per the final working draft; C++29 rows are proposals and direction, not adopted features.