【问题标题】:What are the benefits of using for_each vs. range-for?使用 for_each 与 range-for 有什么好处?
【发布时间】:2015-01-11 23:11:54
【问题描述】:

我知道从历史上看,最好使用标准算法,例如 for_each,而不是 for 循环,因为它们更具可读性。但我只是觉得使用 c++11 的常规 for 循环比具有相应回调函子的众多标准算法要简洁得多。

我这样想有错吗?许多标准算法已经过时了吗?这些方法有哪些不同的好处?

【问题讨论】:

  • 当并行 stl 成为标准时,for_each 将是可并行化的。库结构有时允许比单独的语言功能更多的抽象。所以我认为出于这个原因 for_each 在迁移代码时可能会更好。
  • @GermánDiago 标准中有什么东西可以防止 range-for-loops 的并行化吗?我希望该标准要求“如同常规 for 循环”,但保留实现细节(包括并行化)。
  • 据我所知循环将由实现定义?其实我从来没想过。我所知道的是,您可以使用并行 stl 保证并行化,那么当您可以拥有更安全的东西时,为什么还要依赖实现呢?
  • @GermánDiago 我现在明白你的意思了,谢谢你的澄清。
  • 你可以break一个for循环,而你不能break一个for_each,所以它们不是100%等效的。我会尽可能使用for_each

标签: c++ algorithm c++11


【解决方案1】:

在可读性方面,使用基于范围的 for 现在可能比使用 std::for_each 略好。不过,这并不一定意味着 for_each 已过时。

考虑这段代码:

auto myVals = getManyValsInAContainer();

for (auto& val : myVals)
{
    doStuff(val);
}

它和这个可读性差的代码做同样的事情:

auto myVals = getManyValsInAContainer();

for (auto it = myVals.begin(), end = myVals.end(); it != end; ++it)
{
    doStuff(*it);
}

现在,假设您的容器是std::deque,这意味着您的值位于大块连续内存中,并带有一些指针来跟踪这些块的位置。对于知道实现的人来说,遍历该容器的所有元素很容易:它只需要一个双循环来遍历 each 块的 each 元素。

但是基于范围的呢?它依赖operator++() 从一个元素移动到下一个元素。每次这样做时,operator++() 必须检查下一个元素是否在同一个块中,或者它是否需要继续到下一个块。这些调用彼此完全独立,因此无法提高效率。

(注意:不要尝试手动执行操作:您需要为此访问容器的私有部分,即使您拥有它,您想出的任何东西也只能用于 STL 的某些实现但不是其他人)

那么std::for_each 有什么不同吗?嗯,一方面它是一个函数模板,所以它可以被专门化。它也是 STL 的一部分,这意味着实现它的人确切地知道 std::deque(或您正在使用的任何其他 STL 容器)是如何实现的,并且他们可以使 std::for_each 成为容器类的朋友(或做他们需要做的任何其他事情)以有效地做事,而不是依赖于多次调用operator++()

所以,这就是你的答案:std::for_each 并没有过时。可读性只是它的优点之一,但在迭代 STL 容器时,它仍然比任何 for 循环更有效。

【讨论】:

    【解决方案2】:

    运用你的判断。

    由于 lambdas 和更好的绑定表达式,许多算法在 C++11 中变得更容易使用,这允许您以相对简洁的方式指定函子。但是,基于范围的 for 循环也是一个完全合法的选择。

    如果您只需要循环体中的一两个语句,那么请务必使用基于范围的循环。如果您需要在对象集合上调用成员函数,可以使用for_eachmem_fn。如果绑定表达式看起来足够清晰,请使用它。但无论您做什么,如果您发现自己在一个地方塞入了太多逻辑,请考虑重构并为较小的工作组件赋予可读的名称。

    C++ 为您提供了许多工具,一个工具的存在并不意味着另一个工具是无用的。大型工具带,就像 C++ 一样,面向有经验的用户,而经验会让您为正确的工作选择正确的工具。

    【讨论】:

      【解决方案3】:

      这将是一个相当主观的话题,但我同意你的观点。我认为使用语言结构而不是库结构要好得多,特别是当语言结构简洁易读时。 (尽管在 c++11 之前,for_each 的可读性确实存在争议)

      【讨论】:

      • 我倾向于同意。 OTOH,有了 C++11 的 lambda,现在至少不用写很多函子类了。
      • @ErikAlapää 我讨厌 C++ lambda。 Boost.Phoenix 总是看起来更好,并且导致小函数的语法噪音要少得多。比较 [](int a, int b){return a+b;}_1+_2
      • 有些 lambda 比没有 lambda 好,但我必须同意 @KitsuneYMG。
      猜你喜欢
      • 2017-05-20
      • 1970-01-01
      • 1970-01-01
      • 2020-01-01
      • 2010-10-22
      • 1970-01-01
      • 1970-01-01
      • 2012-03-16
      • 2012-11-28
      相关资源
      最近更新 更多