在迭代已排序的stl容器的同时插入元素

时间:2019-05-04 22:44:42

标签: c++ stl iterator

当我的代码将元素插入到std :: set时进行迭代时,发现了意外的结果。我需要启发。

这是测试代码:

    template<class Ti, class T>
    void iteration_insertion(Ti start, Ti end, T& S){
        for (auto ite=start;ite!=end;++ite){
            auto before=*ite;
            if(*ite % 2)
                S.insert(*ite*2);
            else
                S.insert(*ite/2);
            if(before!=*ite)
                cout<<before<<","<<*ite<<endl;
        }
    }
    void test() {
        set<int> S1({4,7,10,13}),S2(S1);
        cout<<"ascending\n";
        iteration_insertion(S1.begin(),S1.end(),S1);
        cout<<"descending\n";
        iteration_insertion(S2.rbegin(),S2.rend(),S2);
    }

和结果:

ascending
descending
13,26

正如我们看到的那样,迭代器指向的元素有时会在插入后发生更改。但是我不知道什么时候会发生。在测试代​​码中,它只发生过一次,下降了13次。为什么在递增迭代中没有这种不匹配?为什么在降序迭代中7没有不匹配?如何防止它发生?我认为新的附加值可以在以后迭代,这是可以预期的。我只是不想让迭代器因插入而改变。

测试代码可以是一种通用的启发式实践:从每个当前状态生成新状态进行进一步检查。

2 个答案:

答案 0 :(得分:3)

为了说明容器的所有元素,反向迭代器将元素的迭代器存储在取消引用时获得的元素的迭代器之后。例如,rbegin()的结果在内部存储一个最后一遍的迭代器。取消引用时,将创建存储的迭代器的副本,并且该副本将在取消引用之前递减。

本质上是所用标准代码的简化版本:

template<typename Iter>
struct reverse_iterator {
    Iter base;

    auto& operator++() { --base; return *this; }
    auto& operator*() {
        auto copy = base;
        --copy;
        return *copy;
    }
};

auto std::set::rbegin() {
    return reverse_iterator{this->end()};
}

将其应用于您的情况,从rbegin()开始。取消引用将为您提供最后一个元素13。当您插入26时,它将插入在13之后,但在内部存储在ite中的单端迭代器之前。再次取消引用ite时,将生成内部的一个过去的迭代器的副本,该副本递减到13到26之间的位置,然后取消引用以得到26。

以下是说明的图片版本,以帮助您:

diagram showing ite's internal iterator

答案 1 :(得分:0)

一个std::set

  

包含一个排序的集合

值的

。强调“排序”部分。将新值插入集合does not invalidate any existing iterator中,因此根据其自身的优点,插入是有效的。

但是,由于容器 始终 包含一个“已排序”集合,因此,如果新插入的值大于您的迭代器引用的值,则您可以推进迭代器向前,在某个时候,您将遍历新值,因为它现在是集合的一部分,必须始终按排序顺序。

如果新值小于迭代器当前引用的值,则按照定义,向前推进迭代器将不会迭代新值。

没有办法“阻止它发生”,因为根据定义,std::set就是这样工作的。您将需要自己“防止发生”。也许通过将任何新值插入到单独的std::vector中,并在完成迭代之后,再将向量中的累积新值插入到集合中。