C++ Logo

std-proposals

Advanced search

Re: [std-proposals] return value lives until end of scope

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Sat, 23 May 2026 07:36:52 +0000
On Thursday, May 21, 2026, Jonathan Wakely wrote:

>
>
> 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.
>



What if we were to move the 'mark' to the calling site. Perhaps a unary
operator? So the following:

    int main(void)
    {
        %async(Func);
    }

would be the same as:

    int main(void)
    {
        auto dummy = async(Func);
    }

Of course this begs the question, what about loops?

    for ( unsigned i = 0u; i < N; ++i ) %async(Func);

Well I think in the above line, the scope of the return value should be to
the nearest preceding {, meaning that after the loop has finished, you have
N objects on the stack as the N threads run concurrently (implemented by
the compiler by calling 'alloca' inside the loop with linked list).

But what if the loop has a curly-braced body?

    for ( unsigned i = 0u; i < N, ++i )
    {
        %async(Func);
        Log("new thread started");
    }

In the above code, the return value from 'async' will be destroyed at the
}, meaning the threads would run consecutively rather than concurrently. If
you want it to persist up one level, then you double it:

    for ( unsigned i = 0u; i < N, ++i )
    {
        %%async(Func);
        Log("new thread started");
    }

And if you want it to persist up another level above that, you triple it:

    for ( unsigned i = 0u; i < ROWS; ++i )
    {
        for ( unsigned j = 0u; j < COLS; ++j )
        {
            %%%async(Func);
            Log("new thread started");
        }
    }

This could allow some really cool stuff to happen, for instance let's say
we were to give 'std::mutex' a method as follows:

    lock_guard<mutex> mutex::autolock(void)

We could then lock a mutex as follows:

    mutex m;

    int main(void)
    {
        m.%autolock(); // or: %m.autolock() ?
        DoSomething();
    }

We could expand the scope of the lock:

    int main(void)
    {
        if ( a )
        {
            if ( b || c )
            {
                m.%%%autolock(); // or: %%%m.autolock() ?
            }
        }

        DoSomething();
    }

In any function body that contains use of %% or %%%, the compiler would
have to allocate space on the stack upon entering the function in order to
manage the might-or-might-not-be-created objects. This would involve the
use of 'alloca' along with memory addresses of destructors that need to run.

Received on 2026-05-23 07:36:54