23

我想知道人们如何在编码风格方面使用 C++0x lambda。最有趣的问题是在编写捕获列表时要做到多彻底。一方面,该语言允许显式列出捕获的变量,并且通过“显式优于隐式规则”,因此进行详尽的列表以清楚地说明意图是有意义的。例如:

 int sum;
 std::for_each(xs.begin(), xs.end(), [&sum](int x) { sum += x });

另一个论点是,由于 ref-captured locals 的生命周期不会仅仅因为它们被捕获而改变(因此 lambda 很容易最终引用一个生命周期早已结束的局部变量),因此显式捕获有助于减少此类错误并追踪他们。

另一方面,该语言还特意提供了一种快捷方式来自动捕获所有引用的本地变量,因此很明显它是打算使用的。并且可以声称,对于上述示例,即使使用自动捕获也会发生什么,并且 lambda 的生命周期不会超过周围的范围,因此没有理由不使用它:

 int sum;
 std::for_each(xs.begin(), xs.end(), [&](int x) { sum += x });

显然,这不必是全有或全无,但必须有一些基本原理来决定何时自动捕获以及何时明确地进行捕获。有什么想法吗?

同样的另一个问题是何时使用 capture-by-copy - [=],何时使用 capture-by-reference - [&]。Capture-by-copy 显然更安全,因为没有生命周期问题,因此有人可能会争辩说,只要不需要改变捕获的值(或从其他地方查看对其所做的更改),就应该默认使用它,并捕获 -在这种情况下,引用应该被视为(可能为时过早的)优化,仅在明显产生影响的情况下应用。

另一方面,按引用捕获几乎总是更快(特别是因为它通常可以优化到一个副本,如果后者实际上更快,对于小类型和内联模板函数,如大多数 STL 算法),并且是如果 lambda 永远不会超出其范围(这也是所有 STL 算法的情况),则安全,因此在这种情况下默认为按引用捕获是一种微不足道且无害的优化,不会造成伤害。

你觉得呢?你有没有什么想法?

4

5 回答 5

6

我从未听说过“显式优于隐式规则”的规则,我也不同意。当然,在某些情况下它是正确的,但也有很多不是这样的情况。这就是为什么 0xauto毕竟使用关键字添加类型推断的原因。(以及为什么在可能的情况下已经推断出函数模板参数)有很多情况下隐式更可取。

我还没有真正使用过 C++ lambdas(除了使用 VC10 测试版),但我大部分时间都会使用后者

std::for_each(xs.begin(), xs.end(), [&](int x) { sum += x });

我的推理?为什么不这样做?这很方便。有用。而且更容易维护。当我修改 lambda 的主体时,我不必更新捕获列表。为什么我要明确一些编译器比我更了解的东西?编译器可以根据实际使用的内容计算出捕获列表。

至于通过参考与价值捕获?我会应用与常规函数相同的规则。如果您需要引用语义,请按引用捕获。如果您需要复制语义,请执行此操作。如果两者都可以,则更喜欢小类型的价值,如果复制成本高,请参考。

它似乎与您在设计常规函数时必须做出的选择没有什么不同。

我可能应该阅读 lambdas 的规范,但这不是显式捕获列表的主要原因,以便您可以按值捕获一些变量,而通过引用捕获其他变量?

于 2009-07-30T05:51:46.830 回答
2

我最初的直觉是,按价值捕获提供或多或少与 Java 的匿名内部类相同,它们是一个已知数量。但是,当您希望封闭范围可变时,不要使用 array-of-size-1 技巧,而是可以通过引用捕获。然后,您有责任将 lambda 的持续时间限制在引用范围内。

实际上,我同意您的观点,即在处理算法时,通过引用捕获应该是默认设置,我预计这将是大多数用途。Java 中匿名内部类的一个常见用途是侦听器。在 C++ 中可以看到的侦听器样式接口较少,因此它的需求较少,但仍然存在。在这种情况下,最好严格按照价值捕获,以避免出错的机会。shared_ptr 的按值捕获会是一个大习惯吗?

但是,我还没有使用过 lambda,所以我很可能错过了一些巨大的东西。

于 2009-07-30T01:03:22.977 回答
0

我会在方便的时候使用显式捕获列表,当您想要捕获很多变量时(您可能做错了什么并且)您可以使用全部[&]捕获列表。

我对此事的看法是,显式捕获列表是理想的,应该避免隐式变体,并且只有这样人们才不必在实际需要时输入大量代码。

于 2009-08-02T09:40:53.370 回答
0

我可以在这里看到一个新的编码标准规则!;)

这有点做作,但只是为了突出明确的“优势”,请考虑以下几点:

void foo (std::vector<int> v, int x1)
{
  int sum = 0;
  std::for_each (v.begin ()
    , v.end ()
    , [&](int xl) { sum += x1; } 
}

现在,我故意为此选择了糟糕的名字等,但这只是为了说明这一点。如果我们使用显式捕获列表,那么上面的代码将无法编译,但目前可以。

在非常严格的环境(安全至关重要)中,我可以看到这样的规则是编码标准的一部分。

于 2009-07-30T10:41:23.453 回答
0

我正在阅读以下链接以更好地理解 C++ lambda。示例中使用的编码风格非常简洁,我能够遵循: http ://uint32t.blogspot.com/2009/05/using-c0x-lambda-to-replace-boost-bind.html

于 2009-08-03T05:24:49.417 回答