{"id":112632,"date":"2026-08-21T07:00:00","date_gmt":"2026-08-21T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112632"},"modified":"2026-08-21T07:38:00","modified_gmt":"2026-08-21T14:38:00","slug":"20260821-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260821-00\/?p=112632","title":{"rendered":"Reducing C++ template bloat by factoring out the type-dependent portions of the function, practical exam"},"content":{"rendered":"<p>A short time ago, we observed that <a title=\"On wrapping a callable in a lambda that just calls it with the same parameters\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260819-00\/?p=112624\"> there&#8217;s usually no need to wrap a callable in a lambda<\/a>, and more recently observed that <a title=\"Reducing C++ template bloat by factoring out the type-dependent portions of the function\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260820-00\/?p=112629\"> we can apply our principles for reducing C++ template bloat<\/a> to simplify the function further.<\/p>\n<p>Just to refresh our memories, here is where we left off:<\/p>\n<pre>template&lt;typename Lambda&gt;\r\nbool Widget::QueueToWorkerThread(Lambda&amp;&amp; lambda)\r\n{\r\n    CreateWorkerThreadIfNeeded();\r\n    return m_dispatcherQueue.TryEnqueue(\r\n        std::forward&lt;Lambda&gt;(lambda));\r\n}\r\n<\/pre>\n<p>As I noted earlier, lambdas are sort of the worst-case scenario for templated functions since every lambda is a unique type. Every time you call it, you force the generation of a new function.<\/p>\n<p>But we can lift the lambda out of the body and pass it to a worker function. In this case, the only thing we do with the lambda is used it to construct a <code>Dispatcher\u00adQueue\u00adHandler<\/code>, so we can construct the <code>Dispatcher\u00adQueue\u00adHandler<\/code> up front, and use that as the common type.<\/p>\n<pre>namespace winrt\r\n{\r\n    using namespace winrt::Windows::System;\r\n}\r\n\r\nbool Widget::QueueToWorkerThreadWorker(\r\n    winrt::DispatcherQueueHandler const&amp; handler)\r\n{\r\n    CreateWorkerThreadIfNeeded();\r\n    return m_dispatcherQueue.TryEnqueue(handler);\r\n\r\n}\r\n\r\ntemplate&lt;typename Lambda&gt;\r\nbool Widget::QueueToWorkerThread(Lambda&amp;&amp; lambda)\r\n{\r\n    winrt::DispatcherQueueHandler handler(std::forward&lt;Lambda&gt;(lambda));\r\n    return QueueToWorkerThreadWorker(handler);\r\n}\r\n<\/pre>\n<p>our worker function takes the shared type <code>Dispatcher\u00adQueue\u00adHandler<\/code>, and the main function converts the lambda to the shared type, and then calls the non-templated worker function.<\/p>\n<p>The order of operations changes, but it&#8217;s not important whether we construct the <code>Dispatcher\u00adQueue\u00adHandler<\/code> or late. It&#8217;s technically noticeable, because in the event that the <code>Create\u00adWorker\u00adThread\u00adIf\u00adNeeded()<\/code> throws an exception, an rvalue reference to the lambda will be in the moved-from state, but these lambdas are typically created on the fly and discarded, so the caller doesn&#8217;t care whether or not it survives the error. (It&#8217;s also technically noticeable if the creation of the <code>Dispatcher\u00adQueue\u00adHandler<\/code> throws an exception, which means that <code>Create\u00adWorker\u00adThread\u00adIf\u00adNeeded()<\/code> is not called at all. Given what we see of the function, that&#8217;s not going to be a problem either. All it means that we don&#8217;t even bother creating the worker thread.)<\/p>\n<p>But, wait, we can go even further.<\/p>\n<p>We can do the conversion of the lambda to the <code>Dispatcher\u00adQueue\u00adHandler<\/code> directly in the function parameter!<\/p>\n<pre>bool Widget::QueueToWorkerThread(\r\n    winrt::DispatcherQueueHandler const&amp; handler)\r\n{\r\n    CreateWorkerThreadIfNeeded();\r\n    return m_dispatcherQueue.TryEnqueue(handler);\r\n}\r\n<\/pre>\n<p>When the caller passes a lambda, the conversion constructor from the lambda to <code>Dispatcher\u00adQueue\u00adHandler<\/code> kicks in at the call site, so it already arrives at the <code>Queue\u00adTo\u00adWorker\u00adThread<\/code> function in the form of our common type, <code>Dispatcher\u00adQueue\u00adHandler<\/code>.<\/p>\n<p>Hooray, we were able to de-templatize the function entirely.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Applying our principles.<\/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-112632","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Applying our principles.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112632","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=112632"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112632\/revisions"}],"predecessor-version":[{"id":112633,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112632\/revisions\/112633"}],"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=112632"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112632"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112632"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}