{"id":106651,"date":"2022-05-12T07:00:00","date_gmt":"2022-05-12T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106651"},"modified":"2022-05-12T06:10:44","modified_gmt":"2022-05-12T13:10:44","slug":"20220512-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220512-00\/?p=106651","title":{"rendered":"Should I pay attention to the warning that I&#8217;m <CODE>std::move<\/CODE>&#8216;ing from a trivial type? Part 1"},"content":{"rendered":"<p>Say you have a class that is trivial.<\/p>\n<pre>struct widget_id\r\n{\r\n    int value;\r\n};\r\n<\/pre>\n<p>and you decide that you want to <code>std::move<\/code> it around.<\/p>\n<pre>widget_id id = get_widget_id();\r\n\r\nwidget widget = find_widget_by_id(std::move(id));\r\n<\/pre>\n<p>You are using <code>std::move<\/code> because you want to be prepared for the possibility that the <code>widget_id<\/code> might later be changed to something like<\/p>\n<pre>struct widget_id\r\n{\r\n    std::string value;\r\n}\r\n<\/pre>\n<p>In that case, you want to use a <code>std::move<\/code> to avoid a copy.<\/p>\n<p>But using <code>std::move<\/code> on the original integer-based <code>widget_id<\/code> <a href=\"https:\/\/clang.llvm.org\/extra\/clang-tidy\/checks\/performance-move-const-arg.html\"> generates a warning<\/a>:<\/p>\n<blockquote class=\"q\"><p><code>std::move<\/code> of the variable <code>id<\/code> of the trivially-copyable type <code>widget_id<\/code> has no effect.<\/p><\/blockquote>\n<p>What is this warning trying to tell you, and should you care?<\/p>\n<p>The language requires merely that a moved-from object be in a legal (albeit unspecified) state. However, many classes go beyond the bare minimum and define their moved-from state. For example, <code>std::unique_ptr<\/code> specifies that if you move out of a unique pointer, the source is left empty. More generally, all RAII types fall into this category, because moving out of an RAII type transfers the responsibility for the resource to the moved-to object. And most of these RAII types provide a way to inspect whether the RAII wrapper has been absolved of any responsibility.<\/p>\n<p>And that&#8217;s where the warning comes in.<\/p>\n<p>Consider this helper function:<\/p>\n<pre>bool is_empty(widget_id const&amp; id)\r\n{\r\n    return id.value == 0;\r\n    \/\/ -or-\r\n    return id.value.size() == 0;\r\n}\r\n<\/pre>\n<p>This tells you that the <code>widget_id<\/code> doesn&#8217;t actually contain an id after all. Somebody who expects the <code>widget_id<\/code> to be an RAII-style type might do this:<\/p>\n<pre>\/\/ Remember to add power to this widget, if possible\r\nwidget_id id = get_widget_id();\r\n\r\nif (wants_power_early()) {\r\n    add_widget_power(std::move(id));\r\n}\r\n\r\n...\r\n\r\nif (is_empty(id)) {\r\n    \/\/ Nobody added power yet, let's do it now.\r\n    add_widget_power(std::move(id));\r\n}\r\n<\/pre>\n<p>This type of mistake is much more likely if the emptiness check is a member of the <code>widget_id<\/code> itself, either as a named member function or as a boolean conversion operator.<\/p>\n<pre>struct widget_id\r\n{\r\n    int value;\r\n    bool is_empty() const { return value == 0; }\r\n    operator bool() const { return value != 0; }\r\n};\r\n<\/pre>\n<p>Then that last check would be<\/p>\n<pre>if (!id.is_empty()) {\r\n<\/pre>\n<p>or the even more natural-looking<\/p>\n<pre>if (id) {\r\n<\/pre>\n<p>Okay, so maybe you know that you&#8217;re not operating on an RAII type, and that you know that the <code>std::move<\/code> may not actually move anything. Is there some way to avoid having to disable the warning at every single place you do the <code>std::move<\/code>?<\/p>\n<p>One way is to make your type no longer trivial. Probably the simplest way is to give it a user-defined destructor that is equivalent to the trivial destructor.<\/p>\n<pre>struct widget_id\r\n{\r\n    int value;\r\n\r\n    ~widget_id() { } \/\/ no longer a trivial type\r\n};\r\n<\/pre>\n<p>On the other hand, making the type no longer trivial is likely to have unintended cascade effects seeing as triviality affects many other things: If you make a type non-trivial, then you lose the ability to do things like use <code>memcpy<\/code> to copy instances of the type, or use it as a buffer for I\/O operations.<\/p>\n<p>Another option is to route the call through a helper, and then annotate the helper.<\/p>\n<pre>template&lt;typename T&gt;\r\nconstexpr decltype(auto) move_allow_trivial(T&amp;&amp; t) noexcept\r\n{\r\n    return std::move(t); \/\/ NOLINT\r\n}\r\n<\/pre>\n<p>If you don&#8217;t mind that you&#8217;re moving a trivial type, you can call this helper instead of calling <code>std::move<\/code> directly.<\/p>\n<p>There&#8217;s another case for moving from a trivial type. We&#8217;ll look at it next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s just informing you that what you&#8217;re doing may not have the desired effect, depending on what your desired effect was.<\/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-106651","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It&#8217;s just informing you that what you&#8217;re doing may not have the desired effect, depending on what your desired effect was.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106651","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=106651"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106651\/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=106651"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106651"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106651"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}