对于某些人来说,这可能看起来很无聊,但是以下两种 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.
发布评论
评论(9)
第一个版本适用于任何容器,因此在采用任何容器作为参数的模板函数中更有用。 可以想象,即使对于向量来说,它的效率也稍微高一些。
第二个版本仅适用于向量和其他整数索引容器。 对于这些容器来说,它更惯用,对于 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.
如果您不介意(非常?)小的效率损失,我建议使用 Boost.Foreach
If you don't mind a (very?) small loss of efficiency, i'd recommend using Boost.Foreach
方法 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.
以下对标准库容器的迭代方法是最好的。
使用c++11(及其他)的 基于范围的
for
-使用auto
说明符循环:这与您的
方法 0
类似,但需要更少的打字和维护,并且可以与任何与std::begin()
和std::end()
,包括普通旧数组。The following method of iteration over a standard library container is best.
Use c++11 (and beyond)'s range-based
for
-loop with theauto
specifier:This is similar to your
Method 0
but requires less typing, less maintenence and works with any container compatible withstd::begin()
andstd::end()
, including plain-old arrays.方法 0 的更多优点:
容器循环保持不变,
const_iterator 如果你需要,
打字会减少一些代码混乱。
主要缺点是,在许多情况下,您会扫描两个容器,在这种情况下,索引比保留两个迭代器更干净。
Some more advantages of method 0:
container the loop remains the same,
const_iterator if you need,
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.
方法 0,有几个原因。
当然,最好的解决方案是通常是解决方案 2:std 算法之一。 std::for_each、std::transform、std::copy 或您需要的任何其他内容。 这还有一些进一步的优点:
一般来说,避免过度指定您的代码。准确地指定您想要完成的操作,而无需其他任何操作。 std 算法通常是实现这一目标的方法,但即使没有它们,如果您不需要索引
i
,为什么还要使用迭代器呢?相反,在这种情况下。Method 0, for several reasons.
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:
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.巧合的是,我最近做了一个速度测试(用 rand() 填充 10 * 1024 * 1024 个整数)。
这是 3 次运行,时间以纳秒为单位
更新:添加了 stl 算法 std::generate,由于特殊的迭代器优化(VC++2008),它似乎运行最快。 时间以微秒为单位。
结论:使用标准算法,它们可能比显式循环更快! (也是很好的实践)
更新:上面的时间是在 I/O 限制的情况下,我对 CPU 限制做了相同的测试(迭代一个相对较短的向量,它应该重复地适合缓存,将每个元素乘以2 并写回向量)
有趣的是,与 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
UPDATE : added stl-algorithm std::generate, which seems to run the fastest, because of special iterator-optimizing (VC++2008). time in micro-seconds.
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)
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.
这取决于哪种类型的容器。 对于
向量
,使用哪个可能并不重要。 方法 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, beO(N)
, but in fact there is no listat()
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.
上面没有考虑到的一种可能性:根据“做某事”的细节,可以同时拥有方法0和方法1,你不必选择:
这样,查找索引或访问相应的成员都可以轻松获得复杂。
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:
This way, finding the index or accessing the corresponding member are both obtained with trivial complexity.