{"id":106655,"date":"2022-05-13T07:00:00","date_gmt":"2022-05-13T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106655"},"modified":"2022-05-13T06:20:37","modified_gmt":"2022-05-13T13:20:37","slug":"20220513-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220513-00\/?p=106655","title":{"rendered":"Should I pay attention to the warning that I&#8217;m <CODE>std::move<\/CODE>&#8216;ing from a trivial type? Part 2"},"content":{"rendered":"<p>Last time, <a title=\"Should I pay attention to the warning that I'm std::move'ing from a trivial type? Part 1\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220512-00\/?p=106651\"> we looked at motivations for <code>std::move<\/code>&#8216;ing from a trivial type<\/a>. Our investigation looked at the problem through the eyes of the object moved <i>from<\/i>, but there&#8217;s another way to look at the problem, and that&#8217;s from the point of view of the object being moved <i>to<\/i>.<\/p>\n<pre>struct widget_id\r\n{\r\n    int value;\r\n};\r\n\r\nvoid destroy_id(widget_id&amp;&amp; id)\r\n{\r\n    auto value = std::exchange(id.value, 0);\r\n}\r\n<\/pre>\n<p>The intention of the <code>destroy_id<\/code> function is to destroy the <code>id<\/code>. Now, the widget ID is just an integer, so you technically don&#8217;t &#8220;destroy&#8221; it, but the function accepts the <code>widget_id<\/code> by rvalue reference so it can set the <code>value<\/code> of the source to zero, thereby reducing the likelihood that the caller will try to use the <code>id<\/code> later.<\/p>\n<pre>widget_id id = get_id();\r\ndestroy_id(std::move(id));\r\n\r\n\/\/ accidentally use the id\r\nactivate_id(id); \/\/ blatantly invalid id of 0\r\n<\/pre>\n<p>Accepting an rvalue reference helps with two problems: First of all, if the ID is held in a variable, the caller must perform an explicit <code>std::move<\/code>, which tickles the C++ brain cells into recognizing that &#8220;This <code>id<\/code> is consumed by the <code>destroy_id<\/code> function and is therefore not useful any more.&#8221;<\/p>\n<p>Second of all, it means that if the caller messes up and tries to use the <code>id<\/code> after it was moved from, the <code>value<\/code> it contains is guaranteed invalid, instead of possibly using an already-destroyed ID which might by coincidence have been reassigned to another new widget in the meantime. That converts a bug that occurs only if you hit just the right race condition (harder to debug) into a bug that occurs every time guaranteed (easier to debug).<\/p>\n<p>You can even use the &#8220;accept an rvalue when destroying or taking ownership&#8221; trick for primitive types like pointers.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct uniquer_ptr\r\n{\r\n    T* m_ptr = nullptr;\r\n\r\n    uniquer_ptr(T*&amp;&amp; ptr = nullptr)\r\n        : m_ptr(ptr)\r\n    {\r\n        ptr = nullptr;\r\n    }\r\n\r\n    ...\r\n    void reset(T*&amp;&amp; ptr = nullptr)\r\n    {\r\n        T* old_ptr = m_ptr;\r\n        m_ptr = ptr;\r\n        ptr = nullptr;\r\n\r\n        if (old_ptr != nullptr) get_deleter()(old_ptr);\r\n    }\r\n};\r\n\r\n\/\/ or, if you like one-liners...\r\n\r\ntemplate&lt;typename T&gt;\r\nstruct uniquer_ptr\r\n{\r\n    T* m_ptr = nullptr;\r\n\r\n    uniquer_ptr(T*&amp;&amp; ptr = nullptr)\r\n        : m_ptr(std::exchange(ptr, nullprt))\r\n    {\r\n    }\r\n\r\n    ...\r\n    void reset(T*&amp;&amp; ptr = nullptr)\r\n    {\r\n        T* old_ptr = std::exchange(m_ptr, std::exchange(ptr, nullptr));\r\n        if (old_ptr != nullptr) get_deleter()(old_ptr);\r\n    }\r\n};\r\n<\/pre>\n<p>This variant of <code>std::<wbr \/>unique_ptr<\/code> accepts the raw pointer by rvalue reference so that it can null it out, thereby emphasizing that it has taken ownership.<\/p>\n<pre>\/\/ two-timing widget: This compiles\r\n\/\/ and runs, but you crash when ptr\r\n\/\/ destructs.\r\nWidget* widget = new Widget();\r\nauto ptr = std::unique_ptr(widget);\r\nauto ptr2 = std::unique_ptr(widget);\r\n<\/pre>\n<p>Accepting the pointer by rvalue reference reduces the likelihood of two-timing:<\/p>\n<pre>\/\/ doesn't compile\r\nWidget* widget = new Widget();\r\nauto ptr = std::unique_ptr(widget);\r\nauto ptr2 = std::unique_ptr(widget);\r\n\r\n\/\/ compiles but looks suspicious\r\nWidget* widget = new Widget();\r\nauto ptr = uniquer_ptr(std::move(widget));\r\nauto ptr2 = uniquer_ptr(std::move(widget));\r\n<\/pre>\n<p>The revised version looks suspicious because you are using an object (the <code>widget<\/code>) after it has been <code>std::<wbr \/>move<\/code>&#8216;d. At run time, the <code>widget<\/code> is set to <code>nullptr<\/code> by the first <code>uniquer_ptr<\/code> constructor, so <code>ptr2<\/code> constructs from <code>nullptr<\/code> and is consequently empty. There is no crash at runtime. (Though you might scratch your head if you expected <code>ptr2<\/code> to be non-empty.)<\/p>\n<p>Is this a good use for rvalue references to primitive types? I&#8217;m not sure.<\/p>\n<p><b>Bonus chatter<\/b>: Many years ago, the Windows division was given an experimental version\u00b9 of the Visual C++ compiler which treated the argument of the <code>delete<\/code> statement as an lvalue reference if possible, and nulled out the value as part of the deletion.<\/p>\n<pre>int* p = new int();\r\ndelete p;\r\n\/\/ p is now nullptr!\r\n<\/pre>\n<p>The theory behind this change was that the pointer <code>p<\/code> is unusable after being deleted, so the compiler may as well turn it into a provably unusable value, so you can&#8217;t use it by mistake.<\/p>\n<p>Unfortunately, this silent breaking change resulted in some runtime crashes because there were some calling patterns that relied on the old value remaining unchanged, even though it wasn&#8217;t dereferenceable. I forget the details, but I vaguely recall that it involved some sort of reentrancy, and the reentrant call checked the pointer value to see if it had already been processed.<\/p>\n<p>The compiler team backed out the change.<\/p>\n<p>Of course, if you enforce this rule from the start, then mutating the inbound rvalue reference is no longer a breaking change.<\/p>\n<p>\u00b9 One of the ways that the Visual C++ compiler team tests out some of their ideas is to give a copy of their experimental compiler to the Windows team and see what happens. This lets them exercise their compiler with a monstrous real-world code base.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s not about the journey. It&#8217;s about the destination.<\/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-106655","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It&#8217;s not about the journey. It&#8217;s about the destination.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106655","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=106655"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106655\/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=106655"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106655"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106655"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}