C++ STL:哪种 STL 容器迭代方法更好?

发布于 2024-07-16 13:38:47 字数 595 浏览 5 评论 0 原文

对于某些人来说,这可能看起来很无聊,但是以下两种 STL 容器迭代方法中哪一种更好? 为什么

class Elem;
typedef vector<Elem> ElemVec;
ElemVec elemVec;

// Method 0
for (ElemVec::iterator i = elemVec.begin(); i != elemVec.end(); ++i)
{
    Elem& e = *i;
    // Do something
}

// Method 1
for (int i = 0; i < elemVec.size(); ++i)
{
    Elem& e = elemVec.at(i);
    // Do something
}

方法 0 看起来是更干净的 STL,但方法 1 用更少的代码实现了相同的效果。 对容器的简单迭代出现在任何源代码中的各处。 因此,我倾向于选择方法 1,它似乎可以减少视觉混乱和代码大小。

PS:我知道迭代器可以做的不仅仅是简单的索引。 但是,请让回复/讨论集中在容器上的简单迭代,如上所示。

This may seem frivolous to some of you, but which of the following 2 methods of iteration over a STL container is better? Why?

class Elem;
typedef vector<Elem> ElemVec;
ElemVec elemVec;

// Method 0
for (ElemVec::iterator i = elemVec.begin(); i != elemVec.end(); ++i)
{
    Elem& e = *i;
    // Do something
}

// Method 1
for (int i = 0; i < elemVec.size(); ++i)
{
    Elem& e = elemVec.at(i);
    // Do something
}

Method 0 seems like cleaner STL, but Method 1 achieves the same with lesser code. Simple iteration over a container is what appears all over the place in any source code. So, I'm inclined to pick Method 1 which seems to reduce visual clutter and code size.

PS: I know iterators can do much more than a simple index. But, please keep the reply/discussion focused on simple iteration over a container like shown above.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

寄意 2024-07-23 13:38:47

第一个版本适用于任何容器,因此在采用任何容器作为参数的模板函数中更有用。 可以想象,即使对于向量来说,它的效率也稍微高一些。

第二个版本仅适用于向量和其他整数索引容器。 对于这些容器来说,它更惯用,对于 C++ 新手来说很容易理解,并且如果您需要对索引执行其他操作(这并不罕见),那么它会很有用。

像往常一样,恐怕没有简单的“这个更好”的答案。

The first version works with any container and so is more useful in template functions that take any container a s a parameter. It is also conceivably slightly more efficient, even for vectors.

The second version only works for vectors and other integer-indexed containers. It'd somewhat more idiomatic for those containers, will be easily understood by newcomers to C++, and is useful if you need to do something else with the index, which is not uncommon.

As usual, there is no simple "this one is better" answer, I'm afraid.

狠疯拽 2024-07-23 13:38:47

如果您不介意(非常?)小的效率损失,我建议使用 Boost.Foreach

BOOST_FOREACH( Elem& e, elemVec )
{
    // Your code
}

If you don't mind a (very?) small loss of efficiency, i'd recommend using Boost.Foreach

BOOST_FOREACH( Elem& e, elemVec )
{
    // Your code
}
夏有森光若流苏 2024-07-23 13:38:47

方法 0 更快,因此推荐使用。

方法 1 使用 size(),其允许为 O(1),具体取决于容器和 stl 实现。

Method 0 is faster and therefore recommended.

Method 1 uses size() which is allowed to be O(1), depending on the container and the stl implementation.

昔日梦未散 2024-07-23 13:38:47

以下对标准库容器的迭代方法是最好的。

使用(及其他)的 基于范围的 for-使用 auto 说明符循环:

// Method 2
for (auto& e: elemVec)
{
    // Do something with e...
}

这与您的方法 0 类似,但需要更少的打字和维护,并且可以与任何与 std::begin()std::end()包括普通旧数组。

The following method of iteration over a standard library container is best.

Use (and beyond)'s range-based for-loop with the auto specifier:

// Method 2
for (auto& e: elemVec)
{
    // Do something with e...
}

This is similar to your Method 0 but requires less typing, less maintenence and works with any container compatible with std::begin() and std::end(), including plain-old arrays.

梦里兽 2024-07-23 13:38:47

方法 0 的更多优点:

  • 如果从向量移动到另一个向量
    容器循环保持不变,
  • 很容易从迭代器移动到
    const_iterator 如果你需要,
  • 当 c++0x 到来时,auto
    打字会减少一些代码混乱。

主要缺点是,在许多情况下,您会扫描两个容器,在这种情况下,索引比保留两个迭代器更干净。

Some more advantages of method 0:

  • if you move from vector to another
    container the loop remains the same,
  • easy to move from iterator to
    const_iterator if you need,
  • when c++0x will arive, auto
    typing will reduce some of the code clutter.

The main disadvantage is that in many cases you scan two containers, in which case an index is cleaner than keeping two iterators.

只是一片海 2024-07-23 13:38:47

方法 0,有几个原因。

  • 它更好地表达了您的意图,这有助于编译器优化以及可读性
  • 差一错误的可能性较小
  • 即使您用没有运算符[]的列表替换向量它也能工作

当然,最好的解决方案是通常是解决方案 2:std 算法之一。 std::for_each、std::transform、std::copy 或您需要的任何其他内容。 这还有一些进一步的优点:

  • 它更好地表达了您的意图,并允许一些重要的编译器优化(MS 的安全 SCL 对您的方法 0 和 1 执行边界检查,但会在 std 算法上跳过它)
  • 它的代码更少(在调用站点,至少,您必须编写一个函子或其他东西来替换循环体,但是在使用站点,代码会被清理很多,这可能是最重要的地方,

一般来说,避免过度指定您的代码。准确地指定您想要完成的操作,而无需其他任何操作。 std 算法通常是实现这一目标的方法,但即使没有它们,如果您不需要索引 i,为什么还要使用迭代器呢?相反,在这种情况下。

Method 0, for several reasons.

  • It better expresses your intent, which aids compiler optimizations as well as readability
  • Off-by-one errors are less likely
  • It works even if you replace the vector with a list, which doesn't have operator[]

Of course, the best solution will often be solution 2: One of the std algorithms. std::for_each, std::transform, std::copy or whatever else you need. This has some further advantages:

  • It expresses your intent even better, and allows some significant compiler optimizations (MS's secure SCL performs bounds checking on your methods 0 and 1, but will skip it on std algorithms)
  • It's less code (at the call site, at least. Of course you have to write a functor or something to replace the loop body, but at the use site, the code gets cleaned up quite a bit, which is probably where it matters most.

In general, avoid overspecifying your code. Specify exactly what you want done, and nothing else. The std algorithms are usually the way to go there, but even without them, if you don't need the index i, why have it? Use iterators instead, in that case.

樱花细雨 2024-07-23 13:38:47

巧合的是,我最近做了一个速度测试(用 rand() 填充 10 * 1024 * 1024 个整数)。
这是 3 次运行,时间以纳秒为单位

vect[i] time      : 373611869  
vec.at(i) time    : 473297793  
*it = time        : 446818590  
arr[i] time       : 390357294  
*ptr time         : 356895778  

更新:添加了 stl 算法 std::generate,由于特殊的迭代器优化(VC++2008),它似乎运行最快。 时间以微秒为单位。

vect[i] time      : 393951
vec.at(i) time    : 551387
*it = time        : 596080
generate = time   : 346591
arr[i] time       : 375432
*ptr time         : 334612

结论:使用标准算法,它们可能比显式循环更快! (也是很好的实践)

更新:上面的时间是在 I/O 限制的情况下,我对 CPU 限制做了相同的测试(迭代一个相对较短的向量,它应该重复地适合缓存,将每个元素乘以2 并写回向量)

//Visual Studio 2008 Express Edition
vect[i] time      : 1356811
vec.at(i) time    : 7760148
*it = time        : 4913112
for_each = time   : 455713
arr[i] time       : 446280
*ptr time         : 429595

//GCC
vect[i] time      : 431039
vec.at(i) time    : 2421283
*it = time        : 381400
for_each = time   : 380972
arr[i] time       : 363563
*ptr time         : 365971  

有趣的是,与 for_each 相比,VC++ 中的迭代器和运算符 [] 速度要慢得多(这似乎通过一些模板魔法将迭代器降级为指针以提高性能)。
在 GCC 中,at() 的访问时间更差,这是正常的,因为它是测试中唯一进行范围检查的函数。

Coincidentally I made a speed test recently (filling 10 * 1024 * 1024 ints with rand() ).
These are 3 runs, time in nano-seconds

vect[i] time      : 373611869  
vec.at(i) time    : 473297793  
*it = time        : 446818590  
arr[i] time       : 390357294  
*ptr time         : 356895778  

UPDATE : added stl-algorithm std::generate, which seems to run the fastest, because of special iterator-optimizing (VC++2008). time in micro-seconds.

vect[i] time      : 393951
vec.at(i) time    : 551387
*it = time        : 596080
generate = time   : 346591
arr[i] time       : 375432
*ptr time         : 334612

Conclusion : Use standard-algorithms, they might be faster than a explicit loop ! (and also good practice)

Update : the above times were in a I/O-bound situation, I did the same tests with a CPU-bound (iterate over a relatively short vector, which should fit in cache repeatedly, multiply each element by 2 and write back to vector)

//Visual Studio 2008 Express Edition
vect[i] time      : 1356811
vec.at(i) time    : 7760148
*it = time        : 4913112
for_each = time   : 455713
arr[i] time       : 446280
*ptr time         : 429595

//GCC
vect[i] time      : 431039
vec.at(i) time    : 2421283
*it = time        : 381400
for_each = time   : 380972
arr[i] time       : 363563
*ptr time         : 365971  

Interestingly iterators and operator[] is considerably slower in VC++ compared to for_each (which seems to degrade the iterators to pointers through some template-magic for performance).
In GCC access times are only worse for at(), which is normal, because it's the only range-checked function of the tests.

情绪少女 2024-07-23 13:38:47

这取决于哪种类型的容器。 对于向量,使用哪个可能并不重要。 方法 0 已变得更加惯用,但正如大家所说,它们并没有太大区别。

如果您决定使用 list,则方法 1 原则上将是 O(N),但实际上不存在列表 at() 方法,所以你甚至不能那样做。 (所以在某种程度上,你的问题很重要。)

但这本身就是方法 0 的另一个论点:它对不同的容器使用相同的语法。

It depends on which type of container. For a vector, it probably doesn't matter which you use. Method 0 has become more idiomatic, but their isn't a big difference, as everyone says.

If you decided to use a list, instead, method 1 would, in principle, be O(N), but in fact there is no list at() method, so you can't even do it that way. (So at some level your question stacks the deck.)

But that in itself is another argument for method 0: it uses the same syntax for different containers.

心凉怎暖 2024-07-23 13:38:47

上面没有考虑到的一种可能性:根据“做某事”的细节,可以同时拥有方法0和方法1,你不必选择:

for (auto i = elemVec.begin(), ii = 0; ii < elemVec.size(); ++i, ++ii)
{
    // Do something with either the iterator i or the index ii
}

这样,查找索引或访问相应的成员都可以轻松获得复杂。

A possibility not considered above: depending on the details of "Do something", one can have method 0 and method 1 simultaneously, you don't have to choose:

for (auto i = elemVec.begin(), ii = 0; ii < elemVec.size(); ++i, ++ii)
{
    // Do something with either the iterator i or the index ii
}

This way, finding the index or accessing the corresponding member are both obtained with trivial complexity.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文