有什么理由不使用全局 lambda 表达式?

Bar*_*uch 93 c++ lambda

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

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

  // code using bar(x,y) a bunch of times
}
Run Code Online (Sandbox Code Playgroud)

现在 lambda 实现的功能在其他地方变得需要,所以我将把 lambda 提升foo()到全局/命名空间范围之外。我可以将其保留为 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
}
Run Code Online (Sandbox Code Playgroud)

将其更改为适当的函数是微不足道的,但这让我想知道是否有理由将其保留为 lambda?有什么理由不只是到处使用 lambda 而不是“常规”全局函数?

Nic*_*las 66

不使用全局 lambda 的一个非常重要的原因是:因为它不正常。

C++ 的常规函数​​语法从 C 时代就已经存在了。几十年来,程序员已经知道所说的语法意味着什么以及它们是如何工作的(尽管不可否认,整个函数到指针衰减的事情有时甚至会让经验丰富的程序员感到厌烦)。如果一个 C++ 程序员在“完全新手”之外的任何技能水平上看到一个函数定义,他们就会知道他们得到了什么。

全局 lambda 是完全不同的野兽。它具有与常规函数不同的行为。Lambda 是对象,而函数不是。它们有一个类型,但该类型与其函数的类型不同。等等。

所以现在,您提高了与其他程序员交流的标准。如果 C++ 程序员要理解这个函数在做什么,他们就需要理解 lambdas。是的,这是 2019 年,所以一个体面的 C++ 程序员应该知道 lambda 的样子。但它仍然是一个更高的标准。

即使他们理解它,那个程序员心中的问题也会是……为什么这段代码的作者要这样写?如果你对这个问题没有很好的答案(例如,因为你明确想要禁止重载和 ADL,就像在 Ranges 自定义点中一样),那么您应该使用通用机制。

在适当的情况下,更喜欢预期的解决方案而不是新颖的解决方案。使用最简单的方法来表达你的观点。

  • _“自 C 时代以来”_ 在那之前我的朋友 (10认同)
  • @LightnessRaces:我的主要观点是表达 C++ 的函数语法自 C 以来就已经存在,所以很多人通过检查就知道它是什么。 (4认同)
  • 除了第一句话之外,同意所有答案。“这不正常”是一个含糊不清的说法。也许你的意思是“它不常见且令人困惑”? (2认同)

And*_*dyG 53

我可以想到一些您希望避免将全局 lambdas 作为常规函数的替代品的原因:

  • 常规函数可以重载;lambdas 不能(但是有一些技术可以模拟这一点)
  • 尽管它们类似于函数,但即使是像这样的非捕获 lambda 也会占用内存(对于非捕获通常为 1 个字节)。
    • 正如评论中指出的那样,现代编译器将根据as-if规则优化此存储

“为什么我不应该使用 lambda 来替换有状态的函子(类)?”

  • 类只是比 lambdas 具有更少的限制,因此应该是您到达的第一件事
    • (公共/私有数据、重载、辅助方法等)
  • 如果 lambda 有状态,那么就更难以推断它何时变为全局。
    • 我们应该更喜欢在尽可能窄的范围内创建一个类的实例
  • 将非捕获 lambda 转换为函数指针已经很困难,而且 lambda 不可能在其捕获中指定任何内容。
    • 类为我们提供了一种创建函数指针的直接方法,而且它们也是许多程序员更习惯的方式
  • 带有任何捕获的 Lambda 不能被默认构造(在 C++20 中。以前在任何情况下都没有默认构造函数)

  • 另一方面,如果它被传递到某个地方而不是直接调用,那么使用 lambda 而不是函数会促进内联。自然地,人们可以将函数指针打包在“std::integral_constant”中...... (3认同)
  • @AndyG你忘记了as-if规则:不请求 lambda 大小的程序(因为……为什么?!),也不创建指向它的指针,不需要(通常也不需要)为其分配空间。 (2认同)

Mic*_*zel 10

有什么理由不只是到处使用 lambda 而不是“常规”全局函数?

具有一定复杂性的问题需要至少具有相同复杂性的解决方案。但是如果对于同一问题有一个不太复杂的解决方案,那么使用更复杂的解决方案确实没有道理。为什么要引入不需要的复杂性?

在 lambda 和函数之间,函数只是两者中较简单的实体。您不必证明不使用 lambda 是合理的。你必须证明使用一个是合理的。一个 lambda 表达式引入了一个闭包类型,它是一个未命名的类类型,具有所有常用的特殊成员函数、一个函数调用运算符,在这种情况下,一个隐式转换运算符到函数指针,并创建该类型的对象。从 lambda 表达式复制初始化全局变量所做不仅仅是定义一个函数。它定义了一个具有六个隐式声明函数的类类型,定义了另外两个运算符函数,并创建了一个对象。编译器必须做更多的事情。如果您不需要 lambda 的任何功能,则不要使用 lambda……


Bar*_*uch 9

询问后,我想到了一个不这样做的原因:由于这些是变量,它们很容易出现静态初始化顺序失败https://isocpp.org/wiki/faq/ctors#static-init-order),这可能导致错误。

  • 你[可以](/sf/answers/2288812641/)使它们成为`constexpr` lambdas......真正的要点是:只需使用一个函数。 (2认同)

Eri*_*nil 7

Lambda 是匿名函数

如果您使用的是命名的 lambda,则意味着您基本上使用的是命名的匿名函数。为了避免这种矛盾,您不妨使用一个函数。

  • 不。Lambda 是“常规”命名的(通常只是本地命名),这没有什么令人困惑的。 (2认同)

Jar*_*d42 6

如果有什么理由不把它作为一个lambda?有什么理由不只是到处使用 lambda 而不是“常规”全局函数?

我们曾经使用函数而不是全局函子,因此它打破了一致性和最小惊讶原则

主要区别是:

  • 函数可以重载,而函子不能。
  • 函数可以用 ADL 找到,而不是函子。