我想push_back在迭代向量时修改()元素,如下所示:

auto main() -> int {
    std::vector<double> v { 1, 2, 3 };

    for (auto& num : v) {
        std::cout << num << "\n";
        v.push_back(num);
    }

    std::cout << "===========\n";

    for (const auto& num : v) {
        std::cout << num << "\n";
    }
}

输出为:

1
1.00938e-320
0
===========
1
2
3
1
1.00938e-320
0

为什么输出不是这样的:

1
2
3
===========
1
2
3
1
2
3

如果基于的范围只是糖:

for (auto& it{v.begin()}, it != v.end(), it++)

这里不应该有两种可能的情况吗?

  1. v.end()每次迭代都会进行评估,并且永不结束。这应该是一个无限循环。

  2. v.end()在迭代之前评估一次并且应该按我预期输出:

1
2
3
===========
1
2
3
1
2
3

7

  • 9
    基于范围的 for 循环是迭代器的语法糖,而std::vector迭代器在 上会失效push_back。因此,您拥有的是未定义的行为。


    – 

  • 3
    您忘记了向量重新分配的可能性。当重新分配时,所有迭代器都将失效。在向量上编写一个有效的循环来向其添加元素是很棘手的。


    – 


  • 与您的问题无关,但值得一提:“如果基于范围的只是语法糖:for (auto& it{v.begin()}, it != v.end(), it++)”嗯,不完全是。这是正确的等价物:有几个重要的区别,比如end只被评估一次。


    – 


  • 1
    @3CxEZiVlQ dupe 确实相关,但恕我直言,并不完全合适,因为它适用于 C++11,而基于范围的循环在 C++17 中发生了变化(仅在循环之前查询 end() 迭代器)。结果都是 UB,但原因略有不同。


    – 

  • @3CxEZiVlQ 顺便说一句 – 我实际上是在回答之后才意识到差异的(我答案下面的评论让我更深入地了解)。无论如何 – 我会让你决定 C++17 问题是否值得重新打开它。


    – 


最佳答案
1

正如您致电时所看到的

如果操作后新的 size() 大于旧的 capacity() ,则进行重新分配,在这种情况下所有迭代器(包括 end() 迭代器)和对元素的所有引用都将失效。 否则只有end() 迭代器会失效

(重点是我的)

只是使用迭代器的语法糖。

在 期间push_back可能vector需要调整大小并重新分配。在这种情况下,所有迭代器都无效,包括用作循环中的“当前”迭代器。

使用它会导致

即使没有重新分配,检查时也会遇到问题end()

在循环开始前会查询一次。


与 C++17 有细微的差别。但无论如何,
push_back在循环内调用都会调用,因为end()循环前的查询不再有效。

这意味着标准不保证程序的输出。您观察到的任何输出都是合法的。

4

  • 1
    end()每次迭代后与迭代器进行比较”具有误导性。迭代器仅在第一次迭代之前end()确定一次。


    – 

  • @j6t 你是对的,已改正。


    – 

  • 所有基于范围的 for 循环都调用end一次。C++17 中的变化是end现在可以返回具有不同类型的标记。


    – 

  • @PasserBy 再次阅读我发现你是对的——相应地更新了答案。


    –