关于在仅使用相同参数调用时包装可调用对象到Lambda中的问题

The Old New Thing (Raymond Chen) 新闻

摘要

文章解释了当内部Lambda可以直接使用时,将可调用对象包装在Lambda中是不必要的,强调C++中的Lambda只是具有函数调用操作符的类的语法糖。

<p>假设你有一个接受Lambda的函数,并在调用另一个函数时使用它。我见过有人将Lambda包装在另一个Lambda中:</p> <pre>template&lt;typename Lambda&gt; bool Widget::QueueToWorkerThread(Lambda&amp;&amp; lambda) { CreateWorkerThreadIfNeeded(); return m_dispatcherQueue.TryEnqueue( <span style="border: solid 1px currentcolor;">[lambda = std::forward&lt;Lambda&gt;(lambda)]() { lambda(); }</span>); } </pre> <p>但是,如果你只是用与外部Lambda相同的参数调用内部Lambda,那么将Lambda包装在另一个Lambda中是没有意义的。你可以直接使用内部Lambda的函数调用操作符。</p> <pre>template&lt;typename Lambda&gt; bool Widget::QueueToWorkerThread(Lambda&amp;&amp; lambda) { CreateWorkerThreadIfNeeded(); return m_dispatcherQueue.TryEnqueue( <span style="border: solid 1px currentcolor;">std::forward&lt;Lambda&gt;(lambda)</span>); } </pre> <p>我猜有些人没有意识到,Lambda并不是C++语言中的特殊实体,当有人说一个函数接受Lambda时,他们认为这意味着你必须字面上传递一个Lambda。</p> <p>在C++中,Lambda只是一个具有函数调用操作符的类的语法糖。如果你已经有一个具有函数调用操作符的类,就没有必要将其包装在另一个具有相同函数调用操作符的类中。</p> <p>包装Lambda基本上就是在做这件事:</p> <pre>template&lt;typename Lambda&gt; bool Widget::QueueToWorkerThread(Lambda&amp;&amp; lambda) { CreateWorkerThreadIfNeeded(); <span style="border: solid 1px currentcolor; border-bottom: none;">struct wrapper { </span> <span style="border: 1px currentcolor; border-style: none solid;"> wrapper(Lambda&amp;&amp; lambda) : </span> <span style="border: 1px currentcolor; border-style: none solid;"> m_lambda(std::forward&lt;Lambda&gt;(lambda)) {} </span> <span style="border: 1px currentcolor; border-style: none solid;"> auto operator()() const { return m_lambda(); } </span> <span style="border: 1px currentcolor; border-style: none solid;">private: </span> <span style="border: 1px currentcolor; border-style: none solid;"> const std::remove_reference_t&lt;Lambda&gt; m_lambda;</span> <span style="border: solid 1px currentcolor; border-top: none;">}; </span> return m_dispatcherQueue.TryEnqueue( wrapper(std::forward&lt;Lambda&gt;(lambda))); } </pre> <p>不需要引入额外的间接层。传入的Lambda已经是你想要的形式。直接使用它即可。</p> <p><b>额外讨论</b>:如果参数有转换,例如强制将它们转换为特定类型或强制按值传递,那么包装Lambda是有意义的。</p> <p>文章 <a href="https://devblogs.microsoft.com/oldnewthing/20260819-00/?p=112624">关于在仅使用相同参数调用时包装可调用对象到Lambda中的问题</a> 首次发布在 <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>。</p>
查看原文
查看缓存全文

缓存时间: 2026/08/20 15:33

# 关于将可调用对象包装在仅使用相同参数调用它的lambda表达式中——微软经典技术随笔 来源:https://devblogs.microsoft.com/oldnewthing/20260819-00?p=112624 假设你有一个接受lambda表达式的函数,并希望在调用另一个函数时使用它。我曾见过有人将lambda包装在另一个lambda中: ```cpp template<typename Lambda> bool Widget::QueueToWorkerThread(Lambda&& lambda) { CreateWorkerThreadIfNeeded(); return m_dispatcherQueue.TryEnqueue( [lambda = std::forward<Lambda>(lambda)]() { lambda(); }); } ``` 但如果只是用完全相同的参数调用内部lambda,那么将lambda包装在另一个lambda中毫无意义。你可以直接使用内部lambda的函数调用运算符: ```cpp template<typename Lambda> bool Widget::QueueToWorkerThread(Lambda&& lambda) { CreateWorkerThreadIfNeeded(); return m_dispatcherQueue.TryEnqueue( std::forward<Lambda>(lambda)); } ``` 我猜想有些人没有意识到,lambda在C++语言中并非特殊实体。当有人说某个函数接受lambda时,他们可能认为必须字面上传递一个lambda表达式。实际上在C++中,lambda只是一个具有函数调用运算符的类的语法糖。既然你已经拥有一个带函数调用运算符的类,就没必要再把它包装在另一个具有相同函数调用运算符的类中。 包装lambda本质上等同于这样做: ```cpp template<typename Lambda> bool Widget::QueueToWorkerThread(Lambda&& lambda) { CreateWorkerThreadIfNeeded(); struct wrapper { wrapper(Lambda&& lambda) : m_lambda(std::forward<Lambda>(lambda)) {} auto operator()() const { return m_lambda(); } private: const std::remove_reference_t<Lambda> m_lambda; }; return m_dispatcherQueue.TryEnqueue( wrapper(std::forward<Lambda>(lambda))); } ``` 完全没必要引入额外的间接层。传入的lambda已经是你想要的形式,直接使用即可。 **补充说明**:当需要对参数进行转换(如强制转换为特定类型或强制按值传递)时,包装lambda才有意义。 ### 分类 ### 主题 ## 作者 Raymond Chen Raymond参与Windows系统演进已超过30年。2003年,他创立了名为《微软经典技术随笔》的博客,其受欢迎程度远超他最疯狂的想象,这一发展至今仍让他感到惶恐不安。该博客后来衍生出一本同名书籍(Addison Wesley出版社,2007年)。他偶尔会出现在Windows开发文档的Twitter账号上,讲述一些毫无实用信息的故事。

相似文章

C++26:更多函数包装器

Lobsters Hottest

C++26 引入了两个新的函数包装器:std::copyable_function(提供了可复制且 const 正确的 std::function 替代品)和 std::function_ref(一个非拥有、可调用的引用,具有引用语义)。

代码调用就是一切

Reddit r/AI_Agents

认为使用LLM生成的代码调用外部工具(代码调用)比传统的基于JSON的函数调用更高效、功能更强,但需要安全的沙箱环境。作者正在为此方法构建一个框架。

在C++中模拟函数的关键字参数

Hacker News Top

本文演示了一种通过使用带有指定初始化器的结构体来模拟Python风格关键字参数的技术,无需宏或模板魔法即可提高代码可读性。

当编译器让你惊喜

Lobsters Hottest

Matt Godbolt 探讨了编译器优化如何将 O(n) 求和循环转换为 O(1) 的闭式解,突出了 Clang 和 GCC 如何采用循环展开和数学简化等复杂技术来大幅提升代码性能。