August 19th, 2026
heartlikeintriguingcompelling4 reactions

On wrapping a callable in a lambda that just calls it with the same parameters

Suppose you have a function that accepts a lambda and wants to use it when calling another function. I’ve seen people wrap the lambda inside another lambda:

template<typename Lambda>
bool Widget::QueueToWorkerThread(Lambda&& lambda)
{
    CreateWorkerThreadIfNeeded();
    return m_dispatcherQueue.TryEnqueue(
        [lambda = std::forward<Lambda>(lambda)]() { lambda(); });
}

But there’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’s function call operator directly.

template<typename Lambda>
bool Widget::QueueToWorkerThread(Lambda&& lambda)
{
    CreateWorkerThreadIfNeeded();
    return m_dispatcherQueue.TryEnqueue(
        std::forward<Lambda>(lambda));
}

My guess is that some people don’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.

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’s no need to wrap it inside another class with the same function call operator.

Wrapping a lambda is basically doing this:

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)));
}

There’s no need to introduce the extra level of indirection. The incoming lambda is already in the form you want. Just use it.

Bonus chatter: 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.

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.

11 comments

Sort by :
  • Yexuan Xiao · Edited

    @BCS I understand what you are trying to say, but I do not agree with the original description of “pass an overload set.” You have made things clear in your latest comment. Additionally, I consider that when explicitly choosing a particular function, it should be selected using a qualified name, egardless of whether it is being passed as an argument, merely called, or wrapped in a lambda.

  • BCS 1 day ago

    @Yexuan Xiao: I'm confused. Not by what you are writing but by why. You seem to be agreeing with everything I wrote (if so then cool) but it also seems like you are either disagreeing with it or trying to explain something (but without adding anything that I don't already know).

    FWIW, I'm fully aware that there's nothing special going on inside a lambda, but that's actually my point: doing anything with a function name (qualified or not) other than calling it is very hard to do right, so if you need to do something else then the best choice is...

    Read more
  • Yexuan Xiao 2 days ago · Edited

    My comment is a reply to yours, and I am very glad that neither of us missed each other's messages. Your observation is correct, ADL only works for unqualified function call expressions, and static_cast does not participate in that process. ADL is about where to find functions; when it take effects, it produces an overload set. In C++, an overload set is a collection of candidate functions in the same scope (please forget about 'using namespace'). Using static_cast to select a function from an overload set presupposes that you know exactly the scopes in which those functions reside. An unqualified...

    Read more
    • LB

      BCS & Yexuan Xiao, it may interest y’all to know there’s proposals to add new C++ language features for working with overload sets, like declcall (for ADL) and treating overload sets as functors. So, someday, we might actually be able to pass an actual overload set instead of a lambda.

  • BCS · Edited

    @Yexuan Xiao (b/c the comment engine seems to loose track of who a reply is to) Your comment up to the point about static casts is agreeing with my point. However when it comes to static casts, it is very hard to get them to behave the same as calling. One example is that, the best that I can tell, ADL doesn't participate in a static cast so the function that would get called might not even be a candidate in the cast. Also, implicit conversations won't happen.

    Other than using a lambda wrapping just a function call, trying to get...

    Read more