{"id":109865,"date":"2024-06-07T07:00:00","date_gmt":"2024-06-07T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109865"},"modified":"2024-06-09T10:36:11","modified_gmt":"2024-06-09T17:36:11","slug":"20240607-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240607-00\/?p=109865","title":{"rendered":"What&#8217;s the deal with <CODE>std::type_identity<\/CODE>?"},"content":{"rendered":"<p>C++20 has this new thing called <code>std::type_identity<\/code>. Its definition is basically<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct type_identity\r\n{\r\n    using type = T;\r\n};\r\n<\/pre>\n<p>In other words, <code>type_identity&lt;T&gt;::type = T<\/code>.<\/p>\n<p>This sounds profoundly useless. Why take the type <code>T<\/code>, wrap it in another type, and then unwrap it? Why not just use <code>T<\/code> directly?<\/p>\n<p>The primary purpose of <code>type_<wbr \/>identity&lt;T&gt;<\/code> is to allow you to use a type without making it participate in type deduction.<\/p>\n<p>The classic example is a function like, say, <code>add<\/code>:<\/p>\n<pre>template&lt;typename T&gt;\r\nT add(T a, T b)\r\n{\r\n    return a + b;\r\n}\r\n<\/pre>\n<p>This says &#8220;Add two things of the same type and return the result.&#8221;<\/p>\n<p>So what happens if you pass two things of different type?<\/p>\n<pre>auto sum = add(0.5, 1); \/\/ error: cannot deduce T\r\n<\/pre>\n<p>Maybe you want the policy to be &#8220;The type of the first parameter determines what <code>T<\/code> is, and everybody else has to play along.&#8221; You can use <code>type_<wbr \/>identity<\/code> to specify that the second parameter is not deducible.<\/p>\n<pre>template&lt;typename T&gt;\r\nT add(T a, std::type_identity_t&lt;T&gt; b)\r\n{\r\n    return a + b;\r\n}\r\n<\/pre>\n<p>This time, it works:<\/p>\n<pre>auto sum = add(0.5, 1); \/\/ T is \"double\"\r\n<\/pre>\n<p>The compiler deduces <code>T = double<\/code> when it matches <code>0.5<\/code> to the <code>a<\/code> parameter. When it gets around to the second parameter, it is told, &#8220;Just use the <code>T<\/code> you deduced somewhere else.&#8221; So the parameter <code>1<\/code> is treated as a <code>double<\/code>, which involves a numeric conversion.<\/p>\n<p>A more complex case for wanting one parameter&#8217;s type to be dependent upon another parameter&#8217;s type is this example:<\/p>\n<pre>void enqueue(std::function&lt;void(void)&gt; const&amp; work);\r\n\r\ntemplate&lt;typename...Args&gt;\r\nvoid enqueue(std::function&lt;void(Args...)&gt; const&amp; work,\r\n    Args...args)\r\n{\r\n    enqueue([=] { work(args...); });\r\n}\r\n<\/pre>\n<p>The idea here is that you call <code>enqueue<\/code> with a callable and some optional arguments, and the function enqueues the function call to be run somewhere else (maybe on a worker thread). If you pass arguments, then the arguments are passed to your callable.<\/p>\n<p>So you decide to give this function a whirl, but it doesn&#8217;t compile:<\/p>\n<pre style=\"white-space: pre-wrap;\">enqueue([](int v) { std::cout &lt;&lt; v; }, 42);\r\n\r\n\/\/ gcc\r\nerror: no matching function for call to 'enqueue(&lt;lambda(int)&gt;, int)'\r\n   |     enqueue([](int){}, 42);\r\n   |     ~~~~~~~^~~~~~~~~~~~~~~\r\nnote: candidate: 'template&lt;class ... Args&gt; void enqueue(const std::function&lt;void(Args ...)&gt;&amp;, Args ...)'\r\n    | void enqueue(\r\n    |      ^~~~~~~\r\nnote:   template argument deduction\/substitution failed:\r\nnote:   '&lt;lambda(int)&gt;' is not derived from 'const std::function&lt;void(Args ...)&gt;'\r\n   |     enqueue([](int){}, 42);\r\n   |     ~~~~~~~^~~~~~~~~~~~~~~\r\n\r\n\/\/ clang\r\nerror: no matching function for call to 'enqueue'\r\n   |     enqueue([](int){}, 42);\r\n   |     ^~~~~~~\r\nnote: candidate template ignored: could not match 'std::function&lt;void (Args...)&gt;' against '(lambda)'\r\n    7 | void enqueue(\r\n      |      ^\r\n\r\n\/\/ msvc\r\nerror C2672: 'enqueue': no matching overloaded function found\r\nnote: 'void enqueue(const std::function&lt;void(Args...)&gt; &amp;,Args...)': could not deduce template argument for 'const std::function&lt;void(Args...)&gt; &amp;' from '&lt;lambda&gt;'\r\n<\/pre>\n<p>The problem is that template type deduction tries to match types, and it doesn&#8217;t consider conversions. Conversions are handled by a later step (overload resolution). The deduction fails because the lambda is not a <code>std::function<\/code>; the fact that the lambda can be <i>converted to<\/i> a <code>std::function<\/code> is irrelevant.<\/p>\n<p>We can fix this by using <code>type_identity<\/code> to remove the <code>std::function<\/code> from participating in template type deduction.<\/p>\n<pre>template&lt;typename...Args&gt;\r\nvoid enqueue(\r\n    <span style=\"border: solid 1px currentcolor;\">std::type_identity_t&lt;<\/span>\r\n        std::function&lt;void(Args...)&gt;\r\n    <span style=\"border: solid 1px currentcolor;\">&gt;<\/span> const&amp; work,\r\n    Args...args)\r\n{\r\n    enqueue([=] { work(args...); });\r\n}\r\n<\/pre>\n<p>Now the deduction of <code>Args<\/code> is controlled only by the second and subsequent parameters. Once that&#8217;s settled, the compiler then passes the selected function to overload resolution, which says, &#8220;Oh, I can convert this lambda to match the type of the first parameter.&#8221;<\/p>\n<p><b>Bonus chatter<\/b>: You don&#8217;t have to use <code>type_identity<\/code> to wrap the entire type. You can scope it down to something smaller, as long as the type you want to exempt from deduction is inside the template type parameter:<\/p>\n<pre>template&lt;typename...Args&gt;\r\nvoid enqueue(\r\n    std::function&lt;void(\r\n        <span style=\"border: solid 1px currentcolor;\">std::type_identity_t&lt;<\/span>Args<span style=\"border: solid 1px currentcolor;\">&gt;<\/span>...\r\n    )&gt; const&amp; work,\r\n    Args...args)\r\n{\r\n    enqueue([=] { work(args...); });\r\n}\r\n<\/pre>\n<p><b>Bonus bonus chatter<\/b>: In practice, you would probably receive the variadic arguments by universal reference, which means that the <code>std::function<\/code> will probably receive decayed parameters rather than references. The insertion of <code>std::<wbr \/>decay_t<\/code> not only decays the <code>Args<\/code>, but it also prevents them from participating in deduction.<\/p>\n<pre>template&lt;typename...Args&gt;\r\nvoid enqueue(\r\n    std::function&lt;void(\r\n        <span style=\"border: solid 1px currentcolor;\">std::decay_t&lt;<\/span>Args<span style=\"border: solid 1px currentcolor;\">&gt;<\/span>...\r\n    )&gt; const&amp; work,\r\n    Args&amp;&amp;...args)\r\n{\r\n    enqueue([work, ...args = std::forward&lt;Args&gt;(args)]\r\n            { work(std::move(args)...); });\r\n}\r\n<\/pre>\n<p><b>Bonus bonus bonus chatter<\/b>: <a title=\"The identity metafunction\" href=\"https:\/\/open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2018\/p0887r1.pdf\"> The proposal for <code>type_<wbr \/>identity<\/code><\/a> lists some other uses: To force programmers to specify the template type explicitly rather than allowing it to be deduced.<\/p>\n<pre>template&lt;typename T&gt;\r\nvoid must_specialize(std::type_identity_t&lt;T&gt; t);\r\n\r\nmust_specialize(42); \/\/ not allowed\r\nmust_specialize&lt;int&gt;(42); \/\/ must state \"int\" explicitly\r\n<\/pre>\n<p>It can also be used to suppress class template argument deduction (CTAD), and it can be handy as a building block for template metaprogramming. Read the proposal for details.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When you want to use a type without participating in type deduction.<\/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-109865","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>When you want to use a type without participating in type deduction.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109865","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=109865"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109865\/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=109865"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109865"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109865"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}