惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
J
Java Code Geeks
雷峰网
雷峰网
WordPress大学
WordPress大学
L
LangChain Blog
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
博客园 - 三生石上(FineUI控件)
Microsoft Security Blog
Microsoft Security Blog
P
Proofpoint News Feed
腾讯CDC
GbyAI
GbyAI
罗磊的独立博客
Blog — PlanetScale
Blog — PlanetScale
月光博客
月光博客
F
Fortinet All Blogs
Y
Y Combinator Blog
V
V2EX
A
About on SuperTechFans

The Old New Thing

std::call_once vs. std::async - The Old New Thing Magic statics vs. std::call_once - The Old New Thing Why do Microsoft job levels start in the high 50's instead of starting at a sane number like 1? - The Old New Thing Why didn't Read­Directory­ChangesW provide a way to correlate the two sides of a rename operation? - The Old New Thing How can I remove the Close button from my window caption? - The Old New Thing Why is the x86 undefined instruction called ud2? Why 2? - The Old New Thing What algorithm did Windows XP use to choose your initial user picture? - The Old New Thing A sample use of the winstart.bat file in Windows 95 - The Old New Thing Why don't we allow stacks to be sparse, instead of forcing them to be contiguous? - The Old New Thing What happens if you change a window class's GCL_CB­WND­EXTRA? - The Old New Thing The case of the progress callback that never got called when progress happened - The Old New Thing The perils of binding to value types in XAML - The Old New Thing Microspeak: Funded / unfunded - The Old New Thing AWE does not require PAE, though PAE makes it much more useful - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 2 - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 1 - The Old New Thing In the product end game, every change carries significant risk, episode 2 - The Old New Thing Why didn't the Windows Entertainment Pack just run the MS-DOS version inside an emulator? - The Old New Thing Comparing the two holograms on the Windows 95 box - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function - The Old New Thing On wrapping a callable in a lambda that just calls it with the same parameters - The Old New Thing Why did the Microsoft Entertainment Pack for Windows have a special sticker announcing that it also had Tetris? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing Forcing an ARM64X executable to run as a specific architecture - The Old New Thing A little helper class for managing LPPROC_THREAD_ATTRIBUTE_LISTs - The Old New Thing The comments that go into code versus those that go into the pull request description - The Old New Thing The little-known winstart.bat batch file - The Old New Thing How can I perform a Copy­File&shy in unbuffered mode? - The Old New Thing Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 5 - The Old New Thing
Reducing C++ template bloat by factoring out the type-dep...
Raymond Chen · 2026-08-21 · via The Old New Thing

A short time ago, we observed that there’s usually no need to wrap a callable in a lambda, and more recently observed that we can apply our principles for reducing C++ template bloat 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 Chen

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.