在 c++11 中,迭代向量的最现代、最"right"的方法是什么

What is the most modern way, the most "right" way to iterate through a vector, in c++11

本文关键字:c++11 right 方法 是什么 迭代 向量      更新时间:2023-10-16

我现在正在学习c++,我看到了很多遍历向量的方法。我想知道,既然我想用正确的方法学习,那么现在最正确的迭代方法是什么?

取决于以下因素:

    在循环中需要迭代器还是只需要元素?
  1. 你喜欢"几乎总是自动"(AAA)还是"几乎从不自动"(ANA)?

例如,如果您只想遍历std::vector<int> v元素的一些副本,您可以这样做:

for (int element : v) {
   // Use element
}

如果你是AAA级的人,你会写auto element

如果你想避免复制,你需要一个const引用:

for (int const &element : v) {
  // Use element
}

如果你是AAA级玩家,你会写auto const &element

如果你想修改vector中的值,那么你需要一个可修改的引用:

for (int &element : v) {
  // Use element
}

再说一遍,如果你喜欢AAA,那就换成auto &element

如果需要在循环内使用迭代器:

for (std::vector<int>::iterator it = std::begin(v); it != std::end(v); ++it) {
  // Use it
}

如果你是AAA级开发者:

for (auto it = std::begin(v); it != std::end(v); ++it) {
  // Use it
}

请注意,如果循环中的代码不依赖于实际类型(这不是很常见),ANA人员也会使用auto

还要注意,通常可以通过使用<algorithm>中的适当算法来完全避免循环。关于这方面的一些信息,请查看Sean Parent的演讲。他建议任何超过几行的循环都是不好的,通常可以写得更整洁。

没有适用于所有上下文的正确方法。最简单的是for循环:

std::vector<SomeClass> v;
for (SomeClass& sc : v)
   sc.Method();

但在某些情况下std::for_each可能更好,甚至是好的旧for(;;)

使用迭代器…它们是专门为此目的而设计的,适用于所有STL数据结构

http://www.cplusplus.com/reference/vector/vector/begin/

^链接将帮助理解的例子

这取决于你为什么迭代。在很多情况下,根本不应该迭代;使用一种标准算法。否则,如果您只想访问一个容器的所有元素,则范围for是最佳解决方案。如果你想尽早中断迭代,那么最好使用普通的for,除非(通常情况下)你需要在循环之后使用迭代器,在这种情况下,while可能更好;在这两种情况下,都使用迭代器。最后,如果并行迭代多个向量,使用一个索引可能比使用多个迭代器更清晰。

总之,你有这么多选择是有原因的。

我强烈建议使用range-for循环(从c++ 11开始)

vector<int> myVector {0, 1, 2, 3, 4};
for (const auto& i : myVector)
  cout << i << endl;

Pre-C + + 11:

std::for_each(myVector.begin(), myVector.end(), myCallable);

c++ 14将不会添加任何关于此场景的内容;可能c++ 17会,使用并行循环。

如果您需要使用索引,您可以为

选择"raw"。
for (auto i = 0; i < myVector.size(); ++i)

这些是最常见的方法。如果你需要更专业的东西,比如分类、移动、查找,看看<algorithm>

遍历向量的方式取决于迭代的目的。跟随的例子。

标准库算法

如果有一个标准库算法可以满足你的目的,那就使用它。这包括了当操作可以简单地使用lambda、函数或函子表示时的搜索、累积、转换等。通过提供适当的迭代器,它还允许您在您选择的范围内迭代,即不仅仅是beginend

示例:计算一个向量的前20个元素的和

std::vector<int> v = { /* fill as appropriate */ };
int sum_20 = std::accumulate(std::begin(v), std::begin(v)+20, 0);

示例:将每个元素Double并存储在另一个vector

std::vector<int> v = { /* fill as appropriate */ };
std::vector<int> doubled;
doubled.reserve(v.size()); // avoid reallocations
std::transform(std::begin(v), std::end(v), 
               std::back_inserter(doubled), [](int x){return x*2;})

基于范围

的例子:

std::vector<int> v = { /* fill as appropriate */ };
for (int& x : v)
{
    do_something_with(x);
    do_another_thing_with(x);
    if (some_condition(x))
    {
        do_something_else();
    }
}

这对于遍历整个容器非常有效,在这种情况下,您想要执行的操作比您想要在一个简单的lambda中表示的操作更复杂,但又不足以(或使用多次)证明自己的函数。

在某种程度上,您还可以自定义访问元素的方式。如果元素很大,并且只读取它们,则可以指定const foo&:

for (const MyBigObject& : MyBigContainer) { /* ... */ }

如果你需要修改它们,去掉const:

for (MyBigObject& : MyBigContainer) { /* ... */ }

对于小对象,或者需要副本的地方,省略引用:

for (int x : MyVecInt) { /* ... */ }

这允许你为你的访问指定最合适的constness,并控制你是通过引用访问还是通过值访问。通过提供const或非const迭代器,并为提供给接受可调用实体的算法的任何函数/函子/lambda提供适当的参数类型,标准库算法也可以完成类似的事情。

请注意,基于范围的for只会遍历整个容器,并且没有简单的方法可以同时遍历两个或多个容器。与…相比,这限制了它的适用性

对<<p> 常规/strong>

如果需要同时遍历多个容器,或者需要访问索引或迭代器以及元素本身,常规的for循环可能是正确的方法。这是最灵活的,允许你自定义迭代的每一个方面,但它不会利用任何标准库优化,并且通常需要更多的样板来设置迭代器/索引对,检查你是否在容器的范围内,等等。

例子:

std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = {2, 4, 6};
for (std::vector<int>::size_type idx = 0; idx < v1.size(); ++idx)
{
    std::cout << v1[idx] * v2[idx] << ", ";
}
std::vector<int> v1 = {1, 2, 3};
auto iter = v1.cbegin();
const auto end = v1.cend() - 1;
for (; iter != end; ++iter)
{
    std::cout << *iter + *(iter+1) << "n";
}