关于在仅使用相同参数调用时包装可调用对象到Lambda中的问题
摘要
文章解释了当内部Lambda可以直接使用时,将可调用对象包装在Lambda中是不必要的,强调C++中的Lambda只是具有函数调用操作符的类的语法糖。
<p>假设你有一个接受Lambda的函数,并在调用另一个函数时使用它。我见过有人将Lambda包装在另一个Lambda中:</p>
<pre>template<typename Lambda>
bool Widget::QueueToWorkerThread(Lambda&& lambda)
{
CreateWorkerThreadIfNeeded();
return m_dispatcherQueue.TryEnqueue(
<span style="border: solid 1px currentcolor;">[lambda = std::forward<Lambda>(lambda)]() { lambda(); }</span>);
}
</pre>
<p>但是,如果你只是用与外部Lambda相同的参数调用内部Lambda,那么将Lambda包装在另一个Lambda中是没有意义的。你可以直接使用内部Lambda的函数调用操作符。</p>
<pre>template<typename Lambda>
bool Widget::QueueToWorkerThread(Lambda&& lambda)
{
CreateWorkerThreadIfNeeded();
return m_dispatcherQueue.TryEnqueue(
<span style="border: solid 1px currentcolor;">std::forward<Lambda>(lambda)</span>);
}
</pre>
<p>我猜有些人没有意识到,Lambda并不是C++语言中的特殊实体,当有人说一个函数接受Lambda时,他们认为这意味着你必须字面上传递一个Lambda。</p>
<p>在C++中,Lambda只是一个具有函数调用操作符的类的语法糖。如果你已经有一个具有函数调用操作符的类,就没有必要将其包装在另一个具有相同函数调用操作符的类中。</p>
<p>包装Lambda基本上就是在做这件事:</p>
<pre>template<typename Lambda>
bool Widget::QueueToWorkerThread(Lambda&& 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&& lambda) : </span>
<span style="border: 1px currentcolor; border-style: none solid;"> m_lambda(std::forward<Lambda>(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<Lambda> m_lambda;</span>
<span style="border: solid 1px currentcolor; border-top: none;">}; </span>
return m_dispatcherQueue.TryEnqueue(
wrapper(std::forward<Lambda>(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:更多函数包装器
C++26 引入了两个新的函数包装器:std::copyable_function(提供了可复制且 const 正确的 std::function 替代品)和 std::function_ref(一个非拥有、可调用的引用,具有引用语义)。
代码调用就是一切
认为使用LLM生成的代码调用外部工具(代码调用)比传统的基于JSON的函数调用更高效、功能更强,但需要安全的沙箱环境。作者正在为此方法构建一个框架。
在C++中模拟函数的关键字参数
本文演示了一种通过使用带有指定初始化器的结构体来模拟Python风格关键字参数的技术,无需宏或模板魔法即可提高代码可读性。
Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4
Raymond Chen continues his series on creating a fake agile wrapper in COM, explaining how to move non-marshalable objects by forwarding references into the force_marshal wrapper and discussing C++/WinRT deduction guides.
当编译器让你惊喜
Matt Godbolt 探讨了编译器优化如何将 O(n) 求和循环转换为 O(1) 的闭式解,突出了 Clang 和 GCC 如何采用循环展开和数学简化等复杂技术来大幅提升代码性能。