{"id":1645,"date":"2019-10-23T17:29:18","date_gmt":"2019-10-24T00:29:18","guid":{"rendered":"http:\/\/www.elbeno.com\/blog\/?p=1645"},"modified":"2024-05-06T20:07:22","modified_gmt":"2024-05-07T02:07:22","slug":"familiar-template-syntax-iiles","status":"publish","type":"post","link":"https:\/\/www.elbeno.com\/blog\/?p=1645","title":{"rendered":"Familiar Template Syntax IILEs"},"content":{"rendered":"\n<p>A lot has already been said in the blogosphere about the use of immediately-invoked lambda expressions (IILEs) for initialization, and they&#8217;re certainly very useful.<\/p>\n\n\n\n<p>In C++20, <a href=\"https:\/\/wg21.link\/p0428\">P0428<\/a> gives us &#8220;familiar template syntax&#8221; for lambdas. Now, instead of writing a regular generic lambda:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>auto add = &#91;] (auto x, auto y) {\n  return x + y;\n};<\/code><\/pre>\n\n\n\n<p>we have the option to use &#8220;familiar template syntax&#8221; to name the template arguments:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>auto add = &#91;]<typename t=\"\"> &lt;typename T> (T x, T y) {\n  return x + y;\n};<\/typename><\/code><\/pre>\n\n\n\n<p>This has several uses &#8212; see the paper for motivations listed there &#8212; but one I want to draw attention to in particular: when we use an FTSL as an IILE, it can simplify code.<\/p>\n\n\n\n<p>Examples, you say? I have two for you.<\/p>\n\n\n\n<p>First, consider the <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/utility\/apply#Possible_implementation\">&#8220;possible implementation&#8221; of <tt>std::apply<\/tt><\/a> listed on cppreference.com:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace detail {\ntemplate &lt;class F, class Tuple, std::size_t... I>\nconstexpr decltype(auto) apply_impl(F&amp;&amp; f, Tuple&amp;&amp; t, std::index_sequence&lt;I...>)\n{\n    return std::invoke(std::forward&lt;F>(f), std::get&lt;I>(std::forward&lt;Tuple>(t))...);\n}\n}  \/\/ namespace detail\n \ntemplate &lt;class F, class Tuple>\nconstexpr decltype(auto) apply(F&amp;&amp; f, Tuple&amp;&amp; t)\n{\n    return detail::apply_impl(\n        std::forward&lt;F>(f), std::forward&lt;Tuple>(t),\n        std::make_index_sequence&lt;std::tuple_size_v&lt;std::remove_reference_t&lt;Tuple>>>{});\n}<\/code><\/pre>\n\n\n\n<p>With an FTSIILE, that can become:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;class F, class Tuple>\nconstexpr decltype(auto) apply(F&amp;&amp; f, Tuple&amp;&amp; t)\n{\n  return &#91;&amp;] &lt;std::size_t... I> (std::index_sequence&lt;I...>) {\n      return std::invoke(std::forward(f), \n                         std::get&lt;I>(std::forward(t))...);\n  }(std::make_index_sequence&lt;std::tuple_size_v&lt;std::remove_reference_t&lt;Tuple>>>{});\n}<\/code><\/pre>\n\n\n\n<p>With an FTSIILE, we avoid having to <tt>forward<\/tt> multiple times: we can capture by reference into the lambda instead, and do the forwarding once, inside. The result for small functions like this is about half as much code, better encapsulated. No need to declare a helper any more just to destructure template arguments.<\/p>\n\n\n\n<p>Here&#8217;s another quick example, from Jonathan Boccara&#8217;s recent blog post about <a href=\"https:\/\/www.fluentcpp.com\/2019\/03\/08\/stl-algorithms-on-tuples\/\">STL Algorithms on Tuples<\/a>. In that post, Jonathan presents <tt>for_each2<\/tt>, a function template that applies a binary function over two tuples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;class Tuple1, class Tuple2, class F, std::size_t... I>\nF for_each2_impl(Tuple1&amp;&amp; t1, Tuple2&amp;&amp; t2, F&amp;&amp; f, std::index_sequence&lt;I...>)\n{\n    return (void)std::initializer_list&lt;int>{(std::forward&lt;F>(f)(std::get&lt;I>(std::forward&lt;Tuple1>(t1)), std::get&lt;I>(std::forward&lt;Tuple2>(t2))),0)...}, f;\n}\n\ntemplate &lt;class Tuple1, class Tuple2, class F>\nconstexpr decltype(auto) for_each2(Tuple1&amp;&amp; t1, Tuple2&amp;&amp; t2, F&amp;&amp; f)\n{\n    return for_each2_impl(std::forward&lt;Tuple1>(t1), std::forward&lt;Tuple2>(t2), std::forward&lt;F>(f),\n                          std::make_index_sequence&lt;std::tuple_size&lt;std::remove_reference_t&lt;Tuple1>>::value>{});\n}<\/code><\/pre>\n\n\n\n<p>There is a small problem in the original code here: it&#8217;s not right to call <tt>std::forward&lt;F&gt;(f)<\/tt> in <tt>for_each2_impl<\/tt> and potentially move multiple times from the same callable. But that&#8217;s not too important; the function rewritten in C++20 could look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;class Tuple1, class Tuple2, class F>\nconstexpr decltype(auto) for_each2(Tuple1&amp;&amp; t1, Tuple2&amp;&amp; t2, F&amp;&amp; f)\n{\n  return &#91;&amp;] &lt;std::size_t... I> (std::index_sequence&lt;I...>) {\n      return (std::invoke(f, std::get&lt;I>(std::forward(t1)),\n                          std::get&lt;I>(std::forward(t2))), ...), f;\n    }(std::make_index_sequence&lt;std::tuple_size&lt;std::remove_reference_t&lt;Tuple1>>::value>{});\n}<\/code><\/pre>\n\n\n\n<p>Again, less <tt>std::forward<\/tt> boilerplate, better encapsulation, less code overall, and the generated code is identical.\n<\/p>\n\n\n\n<p>So there you have it. Plain IILEs can improve initialization in regular code with less fuss that it would take to extract a small function, and FTSIILEs can now improve template code in the same way, removing the need for separate functions that previously existed only to destructure template arguments and that necessitated more forwarding boilerplate.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A lot has already been said in the blogosphere about the use of immediately-invoked lambda expressions (IILEs) for initialization, and they&#8217;re certainly very useful. In C++20, P0428 gives us &#8220;familiar template syntax&#8221; for lambdas. Now, instead of writing a regular generic lambda: we have the option to use &#8220;familiar template&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22],"tags":[],"class_list":["post-1645","post","type-post","status-publish","format-standard","hentry","category-cpp"],"_links":{"self":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1645","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1645"}],"version-history":[{"count":10,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1645\/revisions"}],"predecessor-version":[{"id":1744,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1645\/revisions\/1744"}],"wp:attachment":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1645"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1645"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1645"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}