The article explains how to reduce C++ template bloat by factoring out type-dependent portions of functions, using a practical example with Windows DispatcherQueueHandler to de-templatize code.
<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’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>
<p>Just to refresh our memories, here is where we left off:</p>
<pre>template<typename Lambda>
bool Widget::QueueToWorkerThread(Lambda&& lambda)
{
CreateWorkerThreadIfNeeded();
return m_dispatcherQueue.TryEnqueue(
std::forward<Lambda>(lambda));
}
</pre>
<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>
<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>DispatcherQueueHandler</code>, so we can construct the <code>DispatcherQueueHandler</code> up front, and use that as the common type.</p>
<pre>namespace winrt
{
using namespace winrt::Windows::System;
}
bool Widget::QueueToWorkerThreadWorker(
winrt::DispatcherQueueHandler const& handler)
{
CreateWorkerThreadIfNeeded();
return m_dispatcherQueue.TryEnqueue(handler);
}
template<typename Lambda>
bool Widget::QueueToWorkerThread(Lambda&& lambda)
{
winrt::DispatcherQueueHandler handler(std::forward<Lambda>(lambda));
return QueueToWorkerThreadWorker(handler);
}
</pre>
<p>our worker function takes the shared type <code>DispatcherQueueHandler</code>, and the main function converts the lambda to the shared type, and then calls the non-templated worker function.</p>
<p>The order of operations changes, but it’s not important whether we construct the <code>DispatcherQueueHandler</code> or late. It’s technically noticeable, because in the event that the <code>CreateWorkerThreadIfNeeded()</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’t care whether or not it survives the error. (It’s also technically noticeable if the creation of the <code>DispatcherQueueHandler</code> throws an exception, which means that <code>CreateWorkerThreadIfNeeded()</code> is not called at all. Given what we see of the function, that’s not going to be a problem either. All it means that we don’t even bother creating the worker thread.)</p>
<p>But, wait, we can go even further.</p>
<p>We can do the conversion of the lambda to the <code>DispatcherQueueHandler</code> directly in the function parameter!</p>
<pre>bool Widget::QueueToWorkerThread(
winrt::DispatcherQueueHandler const& handler)
{
CreateWorkerThreadIfNeeded();
return m_dispatcherQueue.TryEnqueue(handler);
}
</pre>
<p>When the caller passes a lambda, the conversion constructor from the lambda to <code>DispatcherQueueHandler</code> kicks in at the call site, so it already arrives at the <code>QueueToWorkerThread</code> function in the form of our common type, <code>DispatcherQueueHandler</code>.</p>
<p>Hooray, we were able to de-templatize the function entirely.</p>
<p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260821-00/?p=112632">Reducing C++ template bloat by factoring out the type-dependent portions of the function, practical exam</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
# Reducing C++ template bloat by factoring out the type-dependent portions of the function, practical exam - The Old New Thing
Source: [https://devblogs.microsoft.com/oldnewthing/20260821-00?p=112632](https://devblogs.microsoft.com/oldnewthing/20260821-00?p=112632)
A short time ago, we observed that[there’s usually no need to wrap a callable in a lambda](https://devblogs.microsoft.com/oldnewthing/20260819-00/?p=112624), and more recently observed that[we can apply our principles for reducing C\+\+ template bloat](https://devblogs.microsoft.com/oldnewthing/20260820-00/?p=112629)to simplify the function further\.
Just to refresh our memories, here is where we left off:
```
template<typename Lambda>
bool Widget::QueueToWorkerThread(Lambda&& lambda)
{
CreateWorkerThreadIfNeeded();
return m_dispatcherQueue.TryEnqueue(
std::forward<Lambda>(lambda));
}
```
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\.
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`DispatcherQueueHandler`, so we can construct the`DispatcherQueueHandler`up front, and use that as the common type\.
```
namespace winrt
{
using namespace winrt::Windows::System;
}
bool Widget::QueueToWorkerThreadWorker(
winrt::DispatcherQueueHandler const& handler)
{
CreateWorkerThreadIfNeeded();
return m_dispatcherQueue.TryEnqueue(handler);
}
template<typename Lambda>
bool Widget::QueueToWorkerThread(Lambda&& lambda)
{
winrt::DispatcherQueueHandler handler(std::forward<Lambda>(lambda));
return QueueToWorkerThreadWorker(handler);
}
```
our worker function takes the shared type`DispatcherQueueHandler`, and the main function converts the lambda to the shared type, and then calls the non\-templated worker function\.
The order of operations changes, but it’s not important whether we construct the`DispatcherQueueHandler`or late\. It’s technically noticeable, because in the event that the`CreateWorkerThreadIfNeeded\(\)`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’t care whether or not it survives the error\. \(It’s also technically noticeable if the creation of the`DispatcherQueueHandler`throws an exception, which means that`CreateWorkerThreadIfNeeded\(\)`is not called at all\. Given what we see of the function, that’s not going to be a problem either\. All it means that we don’t even bother creating the worker thread\.\)
But, wait, we can go even further\.
We can do the conversion of the lambda to the`DispatcherQueueHandler`directly in the function parameter\!
```
bool Widget::QueueToWorkerThread(
winrt::DispatcherQueueHandler const& handler)
{
CreateWorkerThreadIfNeeded();
return m_dispatcherQueue.TryEnqueue(handler);
}
```
When the caller passes a lambda, the conversion constructor from the lambda to`DispatcherQueueHandler`kicks in at the call site, so it already arrives at the`QueueToWorkerThread`function in the form of our common type,`DispatcherQueueHandler`\.
Hooray, we were able to de\-templatize the function entirely\.
### Category
### Topics
## Author

Raymond has been involved in the evolution of Windows for more than 30 years\. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie\-jeebies\. The Web site spawned a book, coincidentally also titled The Old New Thing \(Addison Wesley 2007\)\. He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information\.
This article explains techniques to reduce C++ template bloat by factoring out type-dependent code into helper objects or using span-based approaches, enhancing code efficiency and maintainability.
The article continues the series on modernizing a 25-year-old minimal C++ unit testing framework, focusing on using inline variables and modules to fix issues with file-specific counters and header dependencies.
This article discusses techniques for writing efficient C++ code, emphasizing data-oriented design and performance optimization for applications like games and real-time processing.
Part 6 of a series on creating an agile Windows Runtime delegate in C++/WinRT, fixing a potential exception issue with std::unique_ptr's custom deleter constructor.
The article discusses a fix for exception safety in C++/WinRT when creating an agile delegate, addressing the issue of unspecified lambda capture construction order to prevent reference leaks.