15

我使用了 C++11 标准提供的新的基于范围的 for 循环,我提出了以下问题:假设我们vector<>使用基于范围的循环for,我们在向量的末尾添加了一些元素本次迭代。因此,循环何时结束?

例如,请参阅以下代码:

#include <iostream>
#include <vector>
using namespace std;
int main() {
    vector<unsigned> test({1,2,3});
    for(auto &num : test) {
        cout << num << " ";
        if(num % 2)
            test.push_back(num + 10);
    }
    cout << "\n";
    for(auto &num : test) 
        cout << num << " ";
    return 0;
}

我使用“-std=c++11”标志测试了 G++ 4.8 和 Apple LLVM 版本 4.2 (clang++),输出为(两者):

1 2 3
1 2 3 11 13

请注意,第一个循环在原始向量的末尾终止,尽管我们向其中添加了其他元素。似乎 for-range 循环仅在开始时评估容器结束。事实上,这是 range-for 的正确行为吗?是委员会规定的吗?我们可以相信这种行为吗?

请注意,如果我们将第一个循环更改为

for(vector<unsigned>::iterator it = test.begin(); it != test.end(); ++it)

迭代器无效并出现分段错误。

4

2 回答 2

20

不,你不能依赖这种行为。修改循环内的向量会导致未定义的行为,因为循环使用的迭代器在修改向量时无效。

基于范围的for循环

for ( range_declaration : range_expression) loop_statement

本质上等同于

{
    auto && __range = range_expression ; 
    for (auto __begin = std::begin(__range),
        __end = std::end(__range); 
        __begin != __end; ++__begin) { 
            range_declaration = *__begin;
            loop_statement 
    }
}

当您修改向量时,迭代器__begin__end不再有效,并且取消引用__begin会导致未定义的行为。

于 2013-06-12T23:02:23.647 回答
0

只要向量的容量足够大,您就可以依赖此行为,因此不需要重新分配。这将保证所有迭代器和引用的有效性,而不是在调用push_back(). 这很好,因为end()在循环开始时只计算了一次(参见std::vector::push_back)。

在您的情况下,您必须增加test向量的容量以保证这种行为:

vector<unsigned> test({1,2,3});
test.reserve(5);

编辑

基于对在基于范围的 for 循环中在该向量上向预分配向量添加元素是否合法的响应,这是未定义行为的情况。使用 gcc、clang 和 cl 构建的发布版本运行良好,但使用 cl (Visual Studio) 构建的调试版本会引发异常。

于 2016-02-17T21:10:32.597 回答