You don't want to count % or the number of scopes, especially if some scopes don't have separate braces.
 

-----Ursprüngliche Nachricht-----
Von: Frederick Virchanza Gotham via Std-Proposals <std-proposals@lists.isocpp.org>
Gesendet: Sa 23.05.2026 09:37
Betreff: Re: [std-proposals] return value lives until end of scope
An: std-proposals@lists.isocpp.org;
CC: Frederick Virchanza Gotham <cauldwell.thomas@gmail.com>;


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.
-- 
 Std-Proposals mailing list
 Std-Proposals@lists.isocpp.org
 https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals