有什么理由不使用全局 lambdas 吗?

我们有一个函数,使用一个非捕获 lambda 内部的自身,例如:

void foo() {
auto bar = [](int a, int b){ return a + b; }


// code using bar(x,y) a bunch of times
}

现在,lambda 实现的功能在其他地方也是需要的,因此我将把 lambda 从 foo()提升到 global/name 空间范围。我可以将它保留为 lambda,使其成为复制粘贴选项,或者将其更改为适当的函数:

auto bar = [](int a, int b){ return a + b; } // option 1
int bar(int a, int b){ return a + b; } // option 2


void foo() {
// code using bar(x,y) a bunch of times
}

将它改变为一个正确的函数是微不足道的,但是它使我想知道是否有某种原因 没有将它作为一个 lambda 离开?有什么理由不在任何地方使用 lambdas 而不使用“常规”全局函数吗?

9453 次浏览

I can think of a few reasons you'd want to avoid global lambdas as drop-in replacements for regular functions:

  • regular functions can be overloaded; lambdas cannot (there are techniques to simulate this, however)
  • Despite the fact that they are function-like, even a non-capturing lambda like this will occupy memory (generally 1 byte for non-capturing).
    • as pointed out in the comments, modern compilers will optimize this storage away under the as-if rule

"Why shouldn't I use lambdas to replace stateful functors (classes)?"

  • classes simply have fewer restrictions than lambdas and should therefore be the first thing you reach for
    • (public/private data, overloading, helper methods, etc.)
  • if the lambda has state, then it is all the more difficult to reason about when it becomes global.
    • We should prefer to create an instance of a class at the narrowest possible scope
  • it's already difficult to convert a non-capturing lambda into a function pointer, and it is impossible for a lambda that specifies anything in its capture.
    • classes give us a straightforward way to create function pointers, and they're also what many programmers are more comfortable with
  • Lambdas with any capture cannot be default-constructed (in C++20. Previously there was no default constructor in any case)

After asking, I thought of a reason to not do this: Since these are variables, they are prone to Static Initialization Order Fiasco (https://isocpp.org/wiki/faq/ctors#static-init-order), which could cause bugs down the line.

There's one very important reason not to use global lambdas: because it's not normal.

C++'s regular function syntax has been around since the days of C. Programmers have known for decades what said syntax means and how they work (though admittedly that whole function-to-pointer decay thing sometimes bites even seasoned programmers). If a C++ programmer of any skill level beyond "utter newbie" sees a function definition, they know what they're getting.

A global lambda is a different beast altogether. It has different behavior from a regular function. Lambdas are objects, while functions are not. They have a type, but that type is distinct from the type of their function. And so forth.

So now, you've raised the bar in communicating with other programmers. A C++ programmer needs to understand lambdas if they're going to understand what this function is doing. And yes, this is 2019, so a decent C++ programmer should have an idea what a lambda looks like. But it is still a higher bar.

And even if they understand it, the question on that programmer's mind will be... why did the writer of this code write it that way? And if you don't have a good answer for that question (for example, because you explicitly want to forbid overloading and ADL, as in Ranges customization points), then you should use the common mechanism.

Prefer expected solutions to novel ones where appropriate. Use the least complicated method of getting your point across.

Is there any reason not to just use lambdas everywhere instead of "regular" global functions?

A problem of a certain level of complexity requires a solution of at least the same complexity. But if there is a less complex solution for the same problem, then there is really no justification for using the more complex one. Why introduce complexity you don't need?

Between a lambda and a function, a function is simply the less complex kind of entity of the two. You don't have to justify not using a lambda. You have to justify using one. A lambda expression introduces a closure type, which is an unnamed class type with all the usual special member functions, a function call operator, and, in this case, an implicit conversion operator to function pointer, and creates an object of that type. Copy-initializing a global variable from a lambda expression simply does a lot more than just defining a function. It defines a class type with six implicitly-declared functions, defines two more operator functions, and creates an object. The compiler has to do a lot more. If you don't need any of the features of a lambda, then don't use a lambda…

if there is some reason not to leave it as a lambda? Is there any reason not to just use lambdas everywhere instead of "regular" global functions?

We used to use functions instead of global functor, so it breaks the coherency and the Principle of least astonishment.

The main differences are:

  • functions can be overloaded, whereas functors cannot.
  • functions can be found with ADL, not functors.

Lambdas are anonymous functions.

If you are using a named lambda, it means you are basically using a named anonymous function. To avoid this oxymoron, you might as well use a function.