std::experi...al::when_all(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– NAME πŸ’–

std::experimental::when_all β€” literally std::experimental::when_all, bestie, the name tells you the whole story ✨

So this page lives in the C++ Standard Library (concurrency TS corner of it, iykyk) and it's all about that when_all life. Buckle up πŸŽ€


✨ SYNOPSIS ✨

Okay so first things first β€” where does this even live?

Defined in header <experimental/future>

And here are the two overloads, in all their glory πŸ‘‘

template< class InputIt >

auto when_all( InputIt first, InputIt last )                    (1)
(concurrency TS)

    -> future<std::vector<typename
std::iterator_traits<InputIt>::value_type>>;
template< class... Futures >

auto when_all( Futures&&... futures )                           (2)
(concurrency TS)

    -> future<std::tuple<std::decay_t<Futures>...>>;

Both of these are concurrency TS, in case the little (concurrency TS) tag wasn't loud enough πŸ’…

πŸ¦‹ What it actually does πŸ¦‹

The whole vibe: you're creating a future object that becomes ready when all of the input futures and shared_futures become ready. All of them. Every last one. Group-project energy but, like, the one time it actually works 🫢

⚠️ Real talk though: the behavior is undefined if any input future or shared_future is invalid. Not "undefined" as in "kinda weird", undefined as in do not do this. Periodt. πŸ’€

Let me break it down more precisely, because you deserve the specifics:

Let Sequence be std::vector<typename std::iterator_traits<InputIt>::value_type> for (1), and std::tuple<std::decay_t<Futures>...> for (2). This function template creates a shared state containing Sequence and returns a future referring to that shared state.

  • Every input future is moved into the corresponding object in the Sequence in the shared state.
  • Every input shared_future is copied to the corresponding object in the Sequence in the shared state.
  • The order of the objects in the Sequence matches the order of the arguments. No shuffling, no vibes-based reordering, just in-order 🌸

🧚 Overload resolution tea 🧚

1) This one does not participate in overload resolution unless InputIt's value type (i.e., typename std::iterator_traits<InputIt>::value_type) is a std::experimental::future or a std::experimental::shared_future.

2) This one does not participate in overload resolution unless every argument is either a (possibly cv-qualified) std::experimental::shared_future or a cv-unqualified std::experimental::future.

And here's the formally-formal version, for the syllabus nerds πŸ“š (affectionate): for every type Fn in Futures, either std::remove_reference_t<Fn> is std::experimental::future<Rn>, or std::decay_t<Fn> is std::experimental::shared_future<Rn>.

πŸ’… After the call πŸ’…

This part is important, don't skip it:

  • Every input future is no longer valid after this call 😭
  • Every input shared_future remains valid πŸ‘‘

Shared futures stay iconic, futures get consumed. That's the deal. πŸ“


🌈 RETURN VALUE 🌈

You get a future referring to the shared state that the call created. That future is always valid(), and it becomes ready when all of the input futures and shared_futures of the call are ready. Like a little readiness committee ⭐

1) If the range is empty (i.e., first == last), the returned future contains an empty vector and is ready immediately β€” no waiting, instant slay ✨

2) If there are no arguments supplied, a future<std::tuple<>> is returned, and it is immediately ready. Zero arguments, zero problems, still valid πŸ’œ


πŸ”₯ Quick recap so it's locked in: when_all waits for everything, moves futures, copies shared_futures, keeps your argument order, and gives you back a future that's always valid(). Invalid inputs = undefined behavior = please don't πŸ™ˆ

Okay that's the whole page, bestie β€” sourced from http://cppreference.com, dated 2024.06.10, and yes this is the std::experi...al::when_all(3) manual page, abridged-name-and-all 😭🫢 go forth and await your futures πŸ’–

πŸ“¦ where this page comes from