在基于范围的循环中向vector添加元素

Add elements to a vector during range-based loop c++11

本文关键字:循环 vector 元素 添加 于范围 范围      更新时间:2023-10-16

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

例如:

#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

注意,第一个循环终止于原vector的末尾,尽管我们向其中添加了其他元素。for-range循环似乎只对容器的开始结束进行求值。事实上,这是range-for的正确行为吗?是委员会指定的吗?我们能相信这种行为吗?

注意,如果我们将第一个循环更改为
for(vector<unsigned>::iterator it = test.begin(); it != test.end(); ++it)

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

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

基于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 
    }
}

当你修改vector时,迭代器__begin__end不再有效,解引用__begin会导致未定义的行为。

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

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

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

编辑

基于以下问题的回应:在基于范围的for循环中向预分配的vector添加元素是否合法?,这是一个未定义行为的例子。使用gcc、clang和cl构建的发布版本运行良好,但使用cl (Visual Studio)构建的调试版本将抛出异常。