C++ Logo

std-proposals

Advanced search

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

From: Sebastian Wittmeier <wittmeier_at_[hidden]>
Date: Sat, 23 May 2026 09:54:30 +0200
In C++ you want to anchor lifetimes to variables in the scope. Creating a new feature circumventing that, weakens a very core C++ philosophy. (way beyond what lifetime extension for rvalues is doing)   You can use a single variable (container for futures) and use it as reference parameter to a function creating your async call. The function would add a new future to your future container. That saves creating individual scoped variables.     -----Ursprüngliche Nachricht----- Von:Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]> Gesendet:Sa 23.05.2026 09:37 Betreff:Re: [std-proposals] return value lives until end of scope An:std-proposals_at_[hidden]; CC:Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>; 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_at_[hidden] https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2026-05-23 07:57:14