{"id":109771,"date":"2024-05-16T07:00:00","date_gmt":"2024-05-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109771"},"modified":"2024-05-16T08:48:55","modified_gmt":"2024-05-16T15:48:55","slug":"20240516-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240516-00\/?p=109771","title":{"rendered":"Pulling a single item from a C++ parameter pack by its index"},"content":{"rendered":"<p>Suppose you have a C++ parameter pack and you want to pluck out an item from it by index.<\/p>\n<pre>template&lt;int index, typename...Args&gt;\r\nvoid example(Args&amp;&amp;... args)\r\n{\r\n    \/\/ how do I access the index'th args parameter?\r\n}\r\n<\/pre>\n<p>One solution is to use <code>std::tie<\/code>:<\/p>\n<pre>template&lt;int index, typename...Args&gt;\r\nvoid example(Args&amp;&amp;... args)\r\n{\r\n    auto&amp; arg = std::get&lt;index&gt;(\r\n        std::tie(args...));\r\n}\r\n<\/pre>\n<p>We expand the parameter pack into the parameters of <code>std::<wbr \/>tie<\/code>, which returns a tuple of lvalue references. We can then pull out the element at the specified index via <code>std::<wbr \/>get&lt;0&gt;()<\/code>.<\/p>\n<p>The catch here is that <code>std::<wbr \/>tie<\/code> always captures the references as lvalue references. If you want to preserve the reference category of the original parameters, you can use <code>std::<wbr \/>forward_<wbr \/>as_<wbr \/>tuple<\/code>:<\/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 = std::get&lt;index&gt;(\r\n        std::forward_as_tuple(\r\n            std::forward&lt;Args&gt;(args)...));\r\n}\r\n<\/pre>\n<p>This time, we apply <code>std::<wbr \/>forward<\/code> to each of the elements of the parameter pack (to preserve the original reference category), then pass them all as parameters to <code>std::<wbr \/>forward_<wbr \/>as_<wbr \/>tuple<\/code>. Unlike <code>std::<wbr \/>tie<\/code>, <code>std::<wbr \/>forward_<wbr \/>as_<wbr \/>tuple<\/code> preserves the reference categories of its arguments, so it produces a tuple of lvalue and rvalue references that match the original parameters. Again, we pull out the element via <code>std::<wbr \/>get&lt;index&gt;()<\/code>, and capture it as an <code>auto&amp;&amp;<\/code> reference, which again preserves the reference category.<\/p>\n<p>For future reference, you can capture the <code>Args<\/code> at a specific index by extracting it from the tuple, or more easily, getting it from the variable.<\/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 = std::get&lt;index&gt;(\r\n        std::forward_as_tuple(\r\n            std::forward&lt;Args&gt;(args)...));\r\n\r\n    \/\/ The hard way\r\n    using Arg = std::tuple_element_t&lt;index,\r\n        std::tuple&lt;Args&amp;&amp;...&gt;&gt;;\r\n\r\n    \/\/ The lazy way\r\n    using Arg = decltype(arg);\r\n}\r\n<\/pre>\n<p><b>Bonus chatter<\/b>: There is a C++26 proposal to add core language support for pulling elements out of a parameter pack by index: <a href=\"https:\/\/wg21.link\/p2662r1\"> Pack Indexing<\/a>. Under the proposed syntax, the above code would be simplified to<\/p>\n<pre>template&lt;int index, typename...Args&gt;\r\nvoid example(Args&amp;&amp;... args)\r\n{\r\n    \/\/ Extract as lvalue\r\n    auto&amp; arg = args...[index];\r\n    using Arg = Args...[index];\r\n\r\n    \/\/ Preserve reference category\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","protected":false},"excerpt":{"rendered":"<p>Combining a few tools to make a new tool.<\/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-109771","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Combining a few tools to make a new tool.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109771","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=109771"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109771\/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=109771"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109771"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109771"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}