{"id":112776,"date":"2026-10-15T07:00:00","date_gmt":"2026-10-15T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112776"},"modified":"2026-10-15T12:14:49","modified_gmt":"2026-10-15T19:14:49","slug":"20261015-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20261015-00\/?p=112776\/","title":{"rendered":"C++ futures part 4: What if I want an immediate on-thread <CODE>std::async<\/CODE>?"},"content":{"rendered":"<p><a title=\"C++ futures part 3: The mental model for std::async\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20261014-00\/?p=112774\"> I noted last time<\/a> that you are not allowed to ask <code>std::<wbr \/>async<\/code> for an immediate on-thread calculation. The C++ library designers probably chose not to support it because there&#8217;s no point: An immediate on-thread calculation is just a function call, so call the function already and leave me out of it.<\/p>\n<p>But maybe you want to do it because you are interfacing with a component that wants a <code>std::<wbr \/>future<\/code>. How could you do it?<\/p>\n<p>One way would be to implement the immediately synchronous case manually by setting a value into a promise.<\/p>\n<pre>template&lt;typename F, typename... Args&gt;\r\nauto\r\n    make_immediate_future(F&amp;&amp; f, Args&amp;&amp;... args)\r\n-&gt; std::future&lt;\r\n        std::invoke_result_t&lt;std::decay_t&lt;F&gt;,\r\n                             std::decay_t&lt;Args&gt;...&gt;&gt;\r\n{\r\n    std::promise&lt;\r\n        std::invoke_result_t&lt;std::decay_t&lt;F&gt;,\r\n                             std::decay_t&lt;Args&gt;...&gt;&gt; p;\r\n    try {\r\n        p.set_value(std::invoke(auto(f), auto(args)...));\r\n    } catch (...) {\r\n        p.set_exception(std::current_exception());\r\n    }\r\n    return p.get_future();\r\n}\r\n<\/pre>\n<p>Or you could make <code>packaged_task<\/code> do the work for you:<\/p>\n<pre>template&lt;typename F, typename... Args&gt;\r\nauto\r\n    make_immediate_future(F&amp;&amp; f, Args&amp;&amp;... args)\r\n-&gt; std::future&lt;\r\n        std::invoke_result_t&lt;std::decay_t&lt;F&gt;,\r\n                             std::decay_t&lt;Args&gt;...&gt;&gt;\r\n{\r\n    using R = std::invoke_result_t&lt;std::decay_t&lt;F&gt;,\r\n                                   std::decay_t&lt;Args&gt;...&gt;;\r\n\r\n    std::packaged_task&lt;R(Args...)&gt; task(f);\r\n    task(args...);\r\n    return task.get_future();\r\n}\r\n<\/pre>\n<p>Yet another way would be to ask for an on-demand in-thread execution, and then immediately demand it.<\/p>\n<pre>template&lt;typename F, typename... Args&gt;\r\nauto\r\n    make_immediate_future(F&amp;&amp; f, Args&amp;&amp;... args)\r\n-&gt; std::future&lt;\r\n        std::invoke_result_t&lt;std::decay_t&lt;F&gt;,\r\n                             std::decay_t&lt;Args&gt;...&gt;&gt;\r\n{\r\n    auto f = std::async(std::launch::deferred,\r\n                        std::forward&lt;F&gt;(f),\r\n                        std::forward&lt;Args&gt;(args)...);\r\n    f.wait();\r\n    return f;\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Providing the case that the C++ language won&#8217;t do.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-112776","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Providing the case that the C++ language won&#8217;t do.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112776","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112776"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112776\/revisions"}],"predecessor-version":[{"id":112777,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112776\/revisions\/112777"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112776"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112776"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112776"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}