在基于范围的循环c ++ 11中向向量添加元素

时间:2013-06-12 22:48:33

标签: c++ for-loop c++11

我使用了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循环仅在开始时评估容器结束。 事实上,这是范围的正确行为吗?是否由委员会指定?我们能相信这种行为吗?

请注意,如果我们通过

更改第一个循环
for(vector<unsigned>::iterator it = test.begin(); it != test.end(); ++it)

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

2 个答案:

答案 0 :(得分:15)

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

基于循环的范围

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会导致未定义的行为。

答案 1 :(得分:0)

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

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

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

修改

根据对Is it legal to add elements to a preallocated vector in a range-based for loop over that vector?的回复,这是一个未定义行为的情况。使用gcc,clang和cl构建的发布版本运行正常,但使用cl(Visual Studio)构建的调试版本将引发异常。