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.
Also sometimes used to convert function calling conventions rather than the language-level parameters 🙂
A related issue is is wrapping a simple function in a lambda. Consider:
<code>
and wanting to use square with modify_and_print. I've seen developers who will reflexively use a lambda <code> when it's perfectly legal to pass the function directly <code>.
That said, sometimes the lambda is useful. If you add an overload for square, the lambda will continue to compiler but the function pointer would need to be cast to choose the correct overload.
Came here looking for that last point. A lambda taking auto and forwarding it to a function symbol is about the only correct way to pass an “overlord set” as an argument.
(C++ more or less requires thinking in terms of overload sets but then sadly provides very limited support for actually doing anything with them but calling them. In fact I’m not actually sure that term is a thing according to the standard.)
We had an intern once who noticed that some of the lambdas they were writing needed to be used across multiple files. They declared global const std::function objects and initialized them from the lambdas. I converted those to normal functions when I saw it…