【问题标题】:When are explicitly-named variable captures necessitated in the definition of a C++11 lambda?C++11 lambda 的定义中何时需要明确命名的变量捕获?
【发布时间】:2019-05-21 12:22:25
【问题描述】:

到目前为止,在查看 C++ lambda expressions in the c++11 style 时,我已将它们全部归为两大类:捕获和非捕获。

非捕获 lambda 虽然在编写方式上受到更多限制,但在使用方式上却更加灵活——它们可以是implicitly converted to analogous function-pointer types;他们不鼓励无偿使用std::function<…>,他们的实现范围是less likely to creep out and cause side-effect problems,等等。

然而,捕获 lambda 可以用更广泛的方式编写。诚然,它们并没有带来我刚才提到的所有这些好处。但是捕获 lambda 弥补了它可以解决的范围广泛的问题,突破了堆栈的前馈函数调用 DAG 的限制,并以无数形式访问周围的范围。

不过,就我的理解而言。当我使用捕获 lambda 时,当我需要一到两个变量时,我倾向于显式引用捕获特定变量:

using lambda_t = std::function<std::add_pointer_t<void>(int)>;

lambda_t lambda_explicit = [&one, &another](int descriptor) {
    return ::mmap(nullptr, one, PROT_READ, MAP_PRIVATE, descriptor, another);
};

...如果有两个以上,我更喜欢(出于同等的句法强迫症和懒惰)避免明确命名的捕获,而支持对所有内容的引用形式:

lambda_t lambda_everything = [&](int descriptor) {
    return ::mmap(nullptr, one, PROT_READ, MAP_PRIVATE, descriptor, another);
};

... 请注意,更改 lambda 捕获的形式并不会改变任何关于 lambda 类型的明显内容——例如,调用签名是相同的。这是违反直觉的,因为 much of the way capturing works is vaguely specified and somewhat implementation-specific 似乎与捕获表达式(或者它是一个声明?还是一个声明列表?...会看到,如果你转到最后一个链接并向下滚动一点。

我什至没有触及绝大多数可能性——我几乎总是只做任何一个:

  1. 根本不捕获;
  2. 一个或两个通过引用捕获的明确命名的变量;或
  3. 不加选择地通过引用捕获所有内容:[&amp;]

在什么情况下我应该特意使用一种捕获形式而不是另一种捕获形式?

哪些形式是特殊情况,一般应避免使用?哪些有明显的惩罚——在性能、代码大小、潜在的 UB 或其他方面?是否有任何捕获表格具有切实和/或简单的好处?

【问题讨论】:

  • 请注意[&amp;] 不会捕获所有内容。它捕获所有使用过的东西。
  • 说实话,我不太确定您要问什么。 FWIW 我通常会分享您的方法,纯粹是因为我喜欢直言不讳,直到它过于繁琐而不值得。 (虽然我在成员函数中也是[=] 的粉丝)
  • "[..] 不要鼓励无偿使用 std::function",你不必使用std::function,你可以简单地使用auto 而不是你不需要的lambda_t
  • @Jarod42 我认为该声明假设了一些尚未传达的上下文。就像存放在容器中一样。您可以(在运行时)将函数指针存储到容器中具有相同签名的不同 lambda 中,但对于不同的捕获 lambda,您不能这样做。
  • 有一个明显的问题,即通过引用捕获会阻止 lambda 在创建引用变量的范围消失后被调用。当 lambda 尝试以任何方式使用现在无效的引用时,任何此类调用都会邀请 UB。

标签: c++ c++11 lambda scope rvalue-reference


【解决方案1】:

在什么情况下我应该特意使用一种捕捉形式而不是另一种?

当您仅限于使用无状态函数时,捕获不是一种选择,您只能使用 1。这通常是在将回调注册到 C 语言 API 时的情况。

当你想将一个对象移动到一个捕获中时,你必须将它移动到一个命名的捕获中,因此被限制为 2。虽然这在 C++11 中是不可能的;您需要来自 C++14 的广义 lambda 捕获。广义捕获对于初始状态不需要与外部通信的可变 lambda 也很有用(除非显式返回)。

此外,当您希望通过值捕获一组变量并通过引用捕获另一组时,默认情况下最多可以捕获一组,并且必须显式捕获至少一组。也就是说,这可能是一个特例。我不记得曾经需要这个。

我认为没有必要使用默认捕获,但它可以节省大量多余的输入。

【讨论】:

    【解决方案2】:

    出于同样的原因,我不喜欢全局变量,我更喜欢使用命名捕获。它为 lambda 代码提供了范围。

    如果有许多变量并且许多 lambda 只使用未命名的语法访问其中一些变量,那么审核代码很快就会变得不可能。好的,如果你有一个带有 3 个局部变量的小函数,那么很容易接受一个 lambda 可以看到所有三个变量,但是如果你有 10 个局部变量,你会在重新访问代码试图了解哪三个变量时浪费时间lambda 实际上会修改。

    【讨论】:

      猜你喜欢
      • 2021-02-07
      • 1970-01-01
      • 1970-01-01
      • 2019-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-18
      • 1970-01-01
      相关资源
      最近更新 更多