代码之家  ›  专栏  ›  技术社区  ›  Pavel Minaev

C++0x LAMBDAS编码方式

  •  22
  • Pavel Minaev  · 技术社区  · 5 年前

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

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

    另一个论点是,由于ref捕获的局部变量的生存期并不会因为它们被捕获而改变(因此lambda可以很容易地引用其生存期已经很长时间结束的局部变量),所以显式捕获有助于减少此类错误并跟踪它们。

    另一方面,该语言还特意为自动捕获所有引用的局部变量提供了一条捷径,因此很明显它是有意使用的。有人可能会说,例如上面的例子,很清楚即使自动捕获也会发生什么,而且lambda的生存期不会超过周围的范围,所以没有理由不使用它:

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

    显然,这不一定是全部或什么都不需要,但必须有一些理由来决定何时自动捕获,以及何时显式执行捕获。有什么想法吗?

    同样的另一个问题是何时使用拷贝捕获- [=] ,以及何时使用引用捕获- [&] . 拷贝捕获显然更安全,因为它不存在生存期问题,因此可以认为,当不需要改变捕获的值(或从其他地方看到对它所做的更改)时,默认情况下应该使用它,在这种情况下,引用捕获应被视为(可能过早)优化,只在明显有区别的地方使用。

    另一方面,对于小型类型和可内联模板函数(如大多数STL算法),通过引用捕获几乎总是更快(尤其是当它通常可以优化到副本时,如果后者实际上更快),并且如果lambda从未超出其作用域(这也是所有STL算法的情况),则是安全的,因此,在本例中,默认通过引用捕获是一个无关紧要且无害的优化,不会造成损害。

    你有什么想法?

    5 回复  |  直到 15 年前
        1
  •  6
  •   jalf    15 年前

    我从来没有听说过“显规则胜于隐规则”的规则,我不同意。当然,有些情况下是真的,但也有很多情况下不是真的 auto 毕竟是关键字。(以及为什么在可能的情况下已经推断出函数模板参数)在很多情况下,隐式参数更可取。

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

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

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

    至于按参考值和价值捕获?我会采用和常规功能相同的规则。如果需要引用语义,请通过引用捕获。如果你需要复制语义,就这么做。如果两种方法都可以,请选择较小类型的值,如果复制成本较高,请选择引用。

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

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

        2
  •  2
  •   Steve Jessop    15 年前

    我最初的直觉是,按值捕获或多或少提供了与Java的匿名内部类相同的数量。但是,如果希望封闭范围可变,则可以通过引用捕获,而不是使用array-of-size-1技巧。因此,您有责任将lambda的持续时间限制在referrand的范围内。

    实际上,我同意你的观点,即在处理算法时,引用捕获应该是默认的,我希望这将是大多数使用。Java中匿名内部类的一个常见用法是侦听器。在C++中可以看到较少的侦听器样式接口,所以它不太需要,但仍然存在。在这种情况下,最好严格按价值捕获,以避免出错的机会。也许,通过共享的价值捕获是一个大的习惯用法吗?

    不过,我还没有用过兰姆达斯,所以我可能错过了一些重要的东西。

        3
  •  0
  •   Richard Corden    15 年前

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

    这有点做作,但只是为了突出一个“优势”,以显式,考虑如下:

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

    现在,我特意选了一些不好的名字等等,但这只是为了说明这一点。如果我们使用一个显式的捕获列表,那么上面的代码将不会编译,但目前它会编译。

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

        4
  •  0
  •   Motti    15 年前

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

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

        5
  •  0
  •   hackworks    15 年前

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