{"id":112624,"date":"2026-08-19T07:00:00","date_gmt":"2026-08-19T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112624"},"modified":"2026-08-19T21:46:37","modified_gmt":"2026-08-20T04:46:37","slug":"20260819-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260819-00\/?p=112624","title":{"rendered":"On wrapping a callable in a lambda that just calls it with the same parameters"},"content":{"rendered":"<p>Suppose you have a function that accepts a lambda and wants to use it when calling another function. I&#8217;ve seen people wrap the lambda inside another lambda:<\/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        <span style=\"border: solid 1px currentcolor;\">[lambda = std::forward&lt;Lambda&gt;(lambda)]() { lambda(); }<\/span>);\r\n}\r\n<\/pre>\n<p>But there&#8217;s no point in wrapping a lambda inside another lambda if you are just calling the inner lambda with the same parameters as the outer one. You can use the inner lambda&#8217;s function call operator directly.<\/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        <span style=\"border: solid 1px currentcolor;\">std::forward&lt;Lambda&gt;(lambda)<\/span>);\r\n}\r\n<\/pre>\n<p>My guess is that some people don&#8217;t realize that a lambda is not a special entity in the C++ language, where if somebody says that a function accepts a lambda, they think that it means that you must literally pass a lambda.<\/p>\n<p>In C++, a lambda is just syntactic sugar for a class with a function call operator. And if you already have a class with a function call operator, there&#8217;s no need to wrap it inside another class with the same function call operator.<\/p>\n<p>Wrapping a lambda is basically doing this:<\/p>\n<pre>template&lt;typename Lambda&gt;\r\nbool Widget::QueueToWorkerThread(Lambda&amp;&amp; lambda)\r\n{\r\n    CreateWorkerThreadIfNeeded();\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">struct wrapper {                                   <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    wrapper(Lambda&amp;&amp; lambda) :                     <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        m_lambda(std::forward&lt;Lambda&gt;(lambda)) {}  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    auto operator()() const { return m_lambda(); } <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">private:                                           <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    const std::remove_reference_t&lt;Lambda&gt; m_lambda;<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">};                                                 <\/span>\r\n    return m_dispatcherQueue.TryEnqueue(\r\n        wrapper(std::forward&lt;Lambda&gt;(lambda)));\r\n}\r\n<\/pre>\n<p>There&#8217;s no need to introduce the extra level of indirection. The incoming lambda is already in the form you want. Just use it.<\/p>\n<p><b>Bonus chatter<\/b>: Wrapping a lambda is significant if there is a transformation on the parameters, such as cocercing them to a particular type or forcing them to be passed by value.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Just use the callable directly.<\/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-112624","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Just use the callable directly.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112624","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=112624"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112624\/revisions"}],"predecessor-version":[{"id":112625,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112624\/revisions\/112625"}],"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=112624"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112624"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112624"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}