Date: Thu, 21 May 2026 14:53:25 +0100
On Thu, 21 May 2026 at 14:34, Andre Kostur via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> int main(void)
> {
> auto const _ = std::async( std::launch::async, lambdaA );
> auto const _ = std::async( std::launch::async, lambdaB );
> auto const _ = std::async( std::launch::async, lambdaC );
> }
>
> Already exists, and does make it more obvious that some state now
> exists for the duration of the scope instead of some invisible
> temporary floating around.
>
> On Thu, May 21, 2026 at 6:17 AM Frederick Virchanza Gotham via
> Std-Proposals <std-proposals_at_[hidden]> wrote:
> >
> >
> > If you look through a codebase and find an invocation of 'std::async'
> with a discarded return value, it's almost certainly a bug.
> >
> > If you write code like the following:
> >
> > int main(void)
> > {
> > std::async( std::launch::async, lambdaA );
> > std::async( std::launch::async, lambdaB );
> > std::async( std::launch::async, lambdaC );
> > }
> >
> > The first thread will complete its execution before the second one
> begins. The third one won't begin its execution until the first two are
> finished. If you want concurrency, you need to do:
> >
> > int main(void)
> > {
> > auto const f1 = std::async( std::launch::async, lambdaA );
> > auto const f2 = std::async( std::launch::async, lambdaB );
> > auto const f3 = std::async( std::launch::async, lambdaC );
> > }
> >
> > We have named f1, f2 and f3 even though we won't use them, so maybe the
> compiler will give a warning for an used variable.
> >
> > Let's simplify the signature of 'std::async' as follows:
> >
> > future<int> async( int(*)(void) );
> >
> > So the problem here is that when you invoke it as follows:
> >
> > std::async( Func );
> >
> > The return value, i.e. the 'future' object, will live until the end of
> the statement. So at the semi-colon, the destructor of the 'future' object
> is invoked -- which waits for the 'int' to become set.
> >
> > But what if we could define the 'async' function in such a way that its
> return value will persist until the end of the current scope -- not until
> the end of the current statement. So the declaration of 'async' becomes:
> >
> > __persistent future<int> async( int(*)(void) );
> >
> > This would mean we would be able to do:
> >
> > int main(void)
> > {
> > std::async( std::launch::async, lambdaA );
> > std::async( std::launch::async, lambdaB );
> > std::async( std::launch::async, lambdaC );
> > }
> >
Yeah, I don't want spooky changes to the lifetime model and how it
interacts with scopes to happen implicitly based on something far away on
the function declaration. If somebody adds __persistent to a declaration,
it silently changes the meaning of existing callers. No thanks.
std-proposals_at_[hidden]> wrote:
> int main(void)
> {
> auto const _ = std::async( std::launch::async, lambdaA );
> auto const _ = std::async( std::launch::async, lambdaB );
> auto const _ = std::async( std::launch::async, lambdaC );
> }
>
> Already exists, and does make it more obvious that some state now
> exists for the duration of the scope instead of some invisible
> temporary floating around.
>
> On Thu, May 21, 2026 at 6:17 AM Frederick Virchanza Gotham via
> Std-Proposals <std-proposals_at_[hidden]> wrote:
> >
> >
> > If you look through a codebase and find an invocation of 'std::async'
> with a discarded return value, it's almost certainly a bug.
> >
> > If you write code like the following:
> >
> > int main(void)
> > {
> > std::async( std::launch::async, lambdaA );
> > std::async( std::launch::async, lambdaB );
> > std::async( std::launch::async, lambdaC );
> > }
> >
> > The first thread will complete its execution before the second one
> begins. The third one won't begin its execution until the first two are
> finished. If you want concurrency, you need to do:
> >
> > int main(void)
> > {
> > auto const f1 = std::async( std::launch::async, lambdaA );
> > auto const f2 = std::async( std::launch::async, lambdaB );
> > auto const f3 = std::async( std::launch::async, lambdaC );
> > }
> >
> > We have named f1, f2 and f3 even though we won't use them, so maybe the
> compiler will give a warning for an used variable.
> >
> > Let's simplify the signature of 'std::async' as follows:
> >
> > future<int> async( int(*)(void) );
> >
> > So the problem here is that when you invoke it as follows:
> >
> > std::async( Func );
> >
> > The return value, i.e. the 'future' object, will live until the end of
> the statement. So at the semi-colon, the destructor of the 'future' object
> is invoked -- which waits for the 'int' to become set.
> >
> > But what if we could define the 'async' function in such a way that its
> return value will persist until the end of the current scope -- not until
> the end of the current statement. So the declaration of 'async' becomes:
> >
> > __persistent future<int> async( int(*)(void) );
> >
> > This would mean we would be able to do:
> >
> > int main(void)
> > {
> > std::async( std::launch::async, lambdaA );
> > std::async( std::launch::async, lambdaB );
> > std::async( std::launch::async, lambdaC );
> > }
> >
Yeah, I don't want spooky changes to the lifetime model and how it
interacts with scopes to happen implicitly based on something far away on
the function declaration. If somebody adds __persistent to a declaration,
it silently changes the meaning of existing callers. No thanks.
Received on 2026-05-21 13:53:44
