{"id":112774,"date":"2026-10-14T07:00:00","date_gmt":"2026-10-14T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112774"},"modified":"2026-10-15T12:11:34","modified_gmt":"2026-10-15T19:11:34","slug":"20261014-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20261014-00\/?p=112774\/","title":{"rendered":"C++ futures part 3: The mental model for <CODE>std::async<\/CODE>"},"content":{"rendered":"<p><a title=\"C++ futures part 2: The mental model for std::promise, std::packaged_task\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20261013-00\/?p=112769\"> We continue our study of C++ futures<\/a> with <code>std::<wbr \/>async<\/code>.<\/p>\n<p>The more complex case is the <code>std::<wbr \/>async<\/code>. You give <code>std::<wbr \/>async<\/code> a callable and optional parameters to pass to that callable, and you also specify how and when you want the callable to be invoked. You get two axes: Do you want it to be calculated immediately or on demand? Do you want the calculation to happen on the thread that requests the value or on its own thread?<\/p>\n<p>Now, you&#8217;d think you could fill out the table like this:<\/p>\n<table style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Immediate<\/th>\n<th>On demand (&#8220;deferred&#8221;)<\/th>\n<\/tr>\n<tr>\n<th>Same thread<\/th>\n<td>Call now and save the value<\/td>\n<td>Call when waited<\/td>\n<\/tr>\n<tr>\n<th>New thread (&#8220;async&#8221;)<\/th>\n<td>Start the thread now<\/td>\n<td>Start the thread when waited<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>However, that&#8217;s not what the C++ standard says.<\/p>\n<p>The C++ standard disallows asking for an immediate value on the same thread. There&#8217;s really no value to doing it that way, since there is no actual &#8220;future&#8221;; it all happens in the present! &#8220;This future could have been a function call.&#8221;<\/p>\n<pre>auto f = std::async({}, calculator);\r\n\r\nauto result = f.get();\r\n<\/pre>\n<p>This would, in theory, mean &#8220;Immediately call the calculator and wait for it to produce a value. Store it in the <code>source<\/code> and return a future that fetches the value from that source.&#8221; But that&#8217;s the same as just<\/p>\n<pre>auto result = calculator();\r\n<\/pre>\n<p>I&#8217;m guessing that the C++ standard disallows it because there&#8217;s no point.<\/p>\n<p>The C++ standard also says that if you combine both <code>std::<wbr \/>launch::<wbr \/>deferred<\/code> and <code>std::<wbr \/>launch::<wbr \/>async<\/code>, it doesn&#8217;t mean that you get a calculation that lazy-starts on its own thread. Instead, the standard says that the implementation can choose which flag to honor and which to ignore.<\/p>\n<p>So really, the table is<\/p>\n<table style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Immediate<\/th>\n<th>On demand (&#8220;deferred&#8221;)<\/th>\n<\/tr>\n<tr>\n<th>Same thread<\/th>\n<td>Illegal<\/td>\n<td>Call when waited<\/td>\n<\/tr>\n<tr>\n<th>New thread (&#8220;async&#8221;)<\/th>\n<td>Start the thread now<\/td>\n<td>Dealer&#8217;s choice<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In all cases, the result of the callable (either a value or an exception) is stored in the <code>source<\/code>.<\/p>\n<p>Now, I noted that there&#8217;s no point to asking <code>std::<wbr \/>async<\/code> for an immediate on-thread calculation. But maybe you want to do it anyway because you are interfacing with some other component that expects to receive a <code>future<\/code>. How could you get the same result?<\/p>\n<p>We&#8217;ll look at that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Doing things later in different ways.<\/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-112774","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Doing things later in different ways.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112774","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=112774"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112774\/revisions"}],"predecessor-version":[{"id":112775,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112774\/revisions\/112775"}],"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=112774"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112774"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112774"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}