{"id":109842,"date":"2024-06-03T07:00:00","date_gmt":"2024-06-03T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109842"},"modified":"2024-06-03T06:56:28","modified_gmt":"2024-06-03T13:56:28","slug":"20240603-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240603-00\/?p=109842","title":{"rendered":"More on harmful overuse of <CODE>std::<WBR>move<\/CODE>"},"content":{"rendered":"<p>Some time ago, I wrote about <a title=\"On harmful overuse of std::move\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20231124-00\/?p=109059\"> harmful overuse of <code>std::<wbr \/>move<\/code><\/a>. Jonathan Duncan asked,<\/p>\n<blockquote class=\"q\"><p>Is there some side-effect or other reason I can&#8217;t see <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20231124-00\/?p=109059#comment-140903\">return std::move(name); case isn&#8217;t possible to elide<\/a>? Or is this just a case of the standards missing an opportunity and compilers being bound to obey the standards?<\/p><\/blockquote>\n<p>In the statement <code>return std::move(name);<\/code>, what the compiler sees is <code>return f(...);<\/code> where <code>f(...)<\/code> is some mysterious function call that returns an rvalue. For all it knows, you could have written <code>return object.<wbr \/>optional_name().<wbr \/>value();<\/code>, which is also a mysterious function call that returns an rvalue. There is nothing in the expression <code>std::move(name)<\/code> that says, &#8220;Trust me, this rvalue that I return is an rvalue of a local variable from this very function!&#8221;<\/p>\n<p>Now, you might say, &#8220;Sure, the compiler doesn&#8217;t know that, but what if we made it know that?&#8221; Make the function <code>std::move<\/code> a magic function, one of the special cases where the core language is in cahoots with the standard library.<\/p>\n<p>This sort of in-cahoots-ness is not unheard of. For example, the compiler has special understanding of <code>std::launder<\/code>, so that it won&#8217;t value-propagate memory values across it, and the compiler has special understanding of memory barriers, so that it won&#8217;t optimize loads and stores across them.<\/p>\n<p>So why not add <code>std::<wbr \/>move<\/code> to the list of functions that the compiler has special understanding of? Technically, this is already permitted by the standard, because the standard requires that any specialization of a templated standard library function &#8220;meets the standard library requirements for the original template,&#8221; so you can&#8217;t write a specialization of <code>std::<wbr \/>move<\/code> that, say, returns a <i>copy<\/i> of the object. However, I think it&#8217;s still legal for the specialization to send angry email to your boss\u00b9 before returning the rvalue reference.<\/p>\n<p>Okay, so we add a new clause to the standard that says that specializations of <code>std::<wbr \/>move<\/code> are disallowed.<\/p>\n<p>This does leave in the lurch alternate implementations of <code>std::<wbr \/>move<\/code>. For example, the Windows Implementation Library (WIL) has its own implementation of <code>std::<wbr \/>move<\/code> called <code>wistd::<wbr \/>move<\/code>. It does this because some of the components that use WIL operate under a constraint that C++ exceptions are disallowed, which means that they cannot <code>#include &lt;memory&gt;<\/code>. But it would also mean that <code>wistd::<wbr \/>move<\/code> is no longer a drop-in replacement for <code>std::<wbr \/>move<\/code>: The compiler would recognize <code>std::<wbr \/>move<\/code> as special, but not <code>wistd::<wbr \/>move<\/code>.<\/p>\n<p>Okay, so we tell those people, &#8220;Oh, stop being such a stick in the mud. Come on in, the water&#8217;s fine! Use <code>std::<wbr \/>move<\/code>!&#8221;<\/p>\n<p>If we operated na\u00efvely, we would say, &#8220;Sure you can return the <code>std::<wbr \/>move<\/code> of a local variable, and we&#8217;ll reuse the return value slot.&#8221; But that would be wrong, because that would be move-constructing an object from another object that resides at the same address, which is not something that happens in normal C++, and I suspect that a lot of move constructors don&#8217;t handle that case. (Not that I expect them to.)<\/p>\n<p>So the C++ language would have to disavow the move constructor at all. It could say that if the <code>return<\/code> statement takes the form <code>return std::move(name)<\/code> where <code>name<\/code> is the name of a local variable eligible for NRVO, then the <code>std::<wbr \/>move<\/code> may be elided.<\/p>\n<p>And maybe to accommodate those people who are afraid of exception-infested waters, you could expand the rule to say that if the compiler can determine that the returned value is an rvalue to a local variable that is eligible for NRVO, then it can be rewritten as returning that local variable via NRVO (while still preserving any other observable behaviors of the relevant expression).<\/p>\n<p>I mean, you <i>could<\/i> do this. Maybe you can even write up a proposal and see what the language committee thinks.<\/p>\n<p>Oh wait, somebody already wrote that proposal! <a title=\"Stop Forcing std::move to Pessimize\" href=\"https:\/\/wg21.link\/p2991r0\"> Stop Forcing <code>std::<wbr \/>move<\/code> to Pessimize<\/a>, which was presented to the C++ standard committee in November 2023, and the response was &#8220;<a href=\"https:\/\/www.reddit.com\/r\/cpp\/comments\/17vnfqq\/202311_kona_iso_c_committee_trip_report_second\/\">Weak consensus, needs more work<\/a>&#8220;.<\/p>\n<p><b>Bonus viewing<\/b>: <a title=\"CppCon 2018: Arthur O'Dwyer &quot;Return Value Optimization: Harder Than It Looks&quot;\" href=\"https:\/\/m.youtube.com\/watch?v=hA1WNtNyNbo\"> CppCon 2018: Arthur O&#8217;Dwyer &#8220;Return Value Optimization: Harder Than It Looks<\/a>&#8220;.<\/p>\n<p>\u00b9 More practical examples would be &#8220;doing performance logging&#8221; or &#8220;doing debug logging&#8221; rather than &#8220;sending angry email to your boss&#8221;.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Could we expand copy elision to cover the harmful cases?<\/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-109842","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Could we expand copy elision to cover the harmful cases?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109842","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=109842"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109842\/revisions"}],"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=109842"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109842"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109842"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}