{"id":103901,"date":"2020-06-23T07:00:00","date_gmt":"2020-06-23T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103883"},"modified":"2020-07-02T06:51:16","modified_gmt":"2020-07-02T13:51:16","slug":"20200623-00-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200623-00\/?p=103901","title":{"rendered":"Mundane std::tuple tricks: Selecting via an index sequence"},"content":{"rendered":"<p>Last time, <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200622-00\/?p=103880\"> we combined tuples<\/a>. That&#8217;s relatively straightforward. Splitting them apart is harder.<\/p>\n<p>The <code>std::index_sequence<\/code> is a standard type which captures a sequence of zero or more nonnegative integers into a type. It&#8217;s a special case of the <code>std::integer_sequence<\/code> which captures integer values of user-specified type, where the user-specified type is a <code>std::size_t<\/code>.<\/p>\n<p>Tuple splitting boils down to a fold expression involving <code>std::index_sequence<\/code>.<\/p>\n<pre>\/\/ Don't use this; see discussion.\r\ntemplate&lt;typename Tuple, std::size_t... Ints&gt;\r\nauto select_tuple(Tuple&amp;&amp; tuple, std::index_sequence&lt;Ints...&gt;)\r\n{\r\n return std::make_tuple(\r\n    std::get&lt;Ints&gt;(std::forward&lt;Tuple&gt;(tuple))...);\r\n}\r\n<\/pre>\n<p>This is the heart of tuple splitting, so let&#8217;s take it apart.<\/p>\n<p>The first template type parameter is the tuple being manipulated. It is captured as a universal reference so that we can forward it. This preserves rvalue-ness, which is particularly important in case some of the types in the tuple are move-only. (It also helps if the types are both copyable and movable, because it will prefer the move, which is usually much less expensive than the copy.)<\/p>\n<p>The remaining template parameters are <code>size_t<\/code> values, representing the indices in the <code>index_sequence<\/code>.<\/p>\n<p>The fold expression is<\/p>\n<pre>    (std::get&lt;Ints&gt;(std::forward&lt;Tuple&gt;(tuple))...)\r\n<\/pre>\n<p>which forms the parameter list to <code>make_tuple<\/code>. The expression is repeated once for each value in the <code>Ints...<\/code> parameter pack, resulting in a series of parameters which pluck the corresponding indexed values from the tuple.<\/p>\n<p>For example,<\/p>\n<pre>auto result = select_tuple(\r\n    std::make_tuple('x', 3.14, 'z'),\r\n    std::index_sequence&lt;2, 1, 1, 2&gt;{});\r\n<\/pre>\n<p>We take a three-element tuple <code>('x', 3.14, 'z')<\/code> and select from it the index sequence <code>&lt;2, 1, 1, 2&gt;<\/code>. The fold expression becomes<\/p>\n<pre>    (std::get&lt;2&gt;(std::forward&lt;Tuple&gt;(tuple)),\r\n     std::get&lt;1&gt;(std::forward&lt;Tuple&gt;(tuple)),\r\n     std::get&lt;1&gt;(std::forward&lt;Tuple&gt;(tuple)),\r\n     std::get&lt;2&gt;(std::forward&lt;Tuple&gt;(tuple)))\r\n<\/pre>\n<p>This extracts items 2, 1, 1, and 2 from the tuple, passing them to <code>make_tuple<\/code>, which recombines them into the resulting tuple <code>('z', 3.14, 3,14, 'z')<\/code>. Note that indices 1 and 2 were extracted multiple times, and index 0 was not extracted at all. Note also that the size of the resulting tuple matches the number of indices, not the size of the source tuple.<\/p>\n<p>Note that if the values in the tuple had been a movable type (not to be confused with <a href=\"https:\/\/en.wikipedia.org\/wiki\/Movable_type\"> movable type<\/a>), then extracting indices <code>&lt;2, 1, 1, 2&gt;<\/code> would have resulted in moving some of the items more than once. That&#8217;s probably not going to produce a happy result, so you don&#8217;t usually extract a value more than once. (Though there&#8217;s nothing stopping you.)<\/p>\n<p>There&#8217;s a defect with our <code>select_<\/code><code>tuple<\/code> function, though. I alluded to it in the comment. We&#8217;ll address that defect next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It all comes down to the index sequence.<\/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-103901","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It all comes down to the index sequence.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103901","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=103901"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103901\/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=103901"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103901"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103901"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}