{"id":110324,"date":"2024-09-30T07:00:00","date_gmt":"2024-09-30T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110324"},"modified":"2024-09-30T08:54:55","modified_gmt":"2024-09-30T15:54:55","slug":"20240930-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240930-00\/?p=110324","title":{"rendered":"Pulling a single item from a C++ parameter pack by its index, remarks"},"content":{"rendered":"<p>In my earlier discussion of <a title=\"Pulling a single item from a C++ parameter pack by its index\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240516-00\/?p=109771\"> pulling a single item from a C++ parameter pack by its index<\/a>, I noted that with the <a href=\"https:\/\/wg21.link\/p2662r1\"> Pack Indexing<\/a> proposal, you would be able to write this to pull an item from a parameter pack while preserving its reference category.<\/p>\n<pre>template&lt;int index, typename...Args&gt;\r\nvoid example(Args&amp;&amp;... args)\r\n{\r\n    auto&amp;&amp; arg = (Args...[index]&amp;&amp;)args...[index];\r\n    using Arg = Args...[index]&amp;&amp;;\r\n}\r\n<\/pre>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240516-00\/?p=109771#comment-141489\"> Why did I need to apply the <code>(Args...[index]&amp;&amp;)<\/code> cast<\/a>? Why can&#8217;t I just write this:<\/p>\n<pre>    auto&amp;&amp; arg = args...[index];\r\n<\/pre>\n<p>or possibly<\/p>\n<pre>    decltype(auto) arg = args...[index];\r\n<\/pre>\n<p>Well, when you write the name of a variable, the result is an lvalue reference, <i>even if the variable is an rvalue reference<\/i>. Watch:<\/p>\n<pre>struct S\r\n{\r\n    S(S&amp;); \/\/ construct from lvalue\r\n    S(S&amp;&amp;); \/\/ construct from rvalue\r\n};\r\n\r\nvoid whathappens(S&amp;&amp; s)\r\n{\r\n    S t = s; \/\/ which will it use?\r\n}\r\n<\/pre>\n<p>Try it out in your favorite compiler. This code constructs from an <i>lvalue<\/i> reference. Even though <code>s<\/code> is an rvalue reference, when you say its name, you get an lvalue reference, so that&#8217;s the construtor that gets selected.<\/p>\n<p>You sort of knew this already. For example, you can&#8217;t take the address of an rvalue reference, but you can write this:<\/p>\n<pre>void whathappens(int&amp;&amp; v)\r\n{\r\n    int* p = &amp;v; \/\/ legal!\r\n}\r\n<\/pre>\n<p>And you&#8217;ve been writing <code>std::<wbr \/>move<\/code> to say &#8220;It&#8217;s okay to move from this object.&#8221;<\/p>\n<pre>void whathappens(S&amp;&amp; s)\r\n{\r\n    S t = std::move(s); \/\/ force rvalue\r\n}\r\n<\/pre>\n<p>I mean, that&#8217;s why you&#8217;ve been writing <code>std::move<\/code> and <code>std::forward<\/code> all these years. If writing <code>s<\/code> already produced an rvalue reference, then there would be no need to <code>std::move<\/code> or <code>std::forward<\/code> it.<\/p>\n<p>&#8220;Okay, I get it. Writing the name of a variable that represents an rvalue reference produces an lvalue. So what?&#8221;<\/p>\n<p>Since writing the name of the variable produces an lvalue reference, <code>decltype(auto)<\/code> sees that the right hand side is an lvalue reference, so it deduces an lvalue reference.<\/p>\n<p>Now, you could say &#8220;Well, sure, but let&#8217;s make a special case for indexed elements from a parameter pack, so that saying their name produces an rvalue reference if the corresponding parameter is an rvalue.&#8221; But that creates another weird special case in C++, and C++ is hard enough to language-lawyer without adding <i>even more<\/i> weird special cases.\u00b9<\/p>\n<p><b>Bonus chatter<\/b>: Instead of<\/p>\n<pre>    auto&amp;&amp; arg = (Args...[index]&amp;&amp;)args...[index];\r\n<\/pre>\n<p>I could also have written<\/p>\n<pre>    auto&amp;&amp; arg = std::forward&lt;Args...[index]&gt;(args...[index]);\r\n<\/pre>\n<p>which is wordier but probably clearer. I was sort of assuming people understood this common shortcut.<\/p>\n<p>\u00b9 I generally believe in the principle that it is better to have a set of simple rules that are easy to understand and explain, even if it means that some scenarios are awkward or suboptimal, as opposed to a set of rules that cover all scenarios but which are so complex that nobody can understand them, much less explain them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Why such a complicated way to pull the type from the pack?<\/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-110324","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Why such a complicated way to pull the type from the pack?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110324","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=110324"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110324\/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=110324"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110324"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110324"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}