标准如何处理容器插入函数中的自引用迭代器?

时间:2013-05-12 14:41:38

标签: c++

在处理像std::vector这样的C ++标准库容器时,它们基于范围的插入方法如何使用引用向量自身内容的迭代器来处理用户?

大概如果他们已经说过vector::iterator,那么实现可以特殊情况下这个场景,但如果它们是用户定义的类型,最终导致访问向量,那么向量如何处理保持那些迭代器在评估范围时有效吗?标准是否仅禁止引用范围内的向量?

举一个简单的例子,考虑一个其value_type为size_t的迭代器,并且取消引用它的结果就是插入的向量的大小。

struct silly_iterator {
    vector<std::size_t>* v;
    unsigned number;
    std::size_t operator*() { return v->size(); }
    operator++() { --number; }
    bool operator==(silly_iterator other) const { return number == 0; }
    // other methods
};
std::vector<std::size_t> vec = { 3, 4, 5, 6, 7 };
vector.insert(vector.begin() + 2, silly_iterator(&vec, 10), silly_iterator());

此代码执行后vec的内容是什么?

另一个例子,

struct silly_iterator { 
    std::vector<std::size_t>* v; 
    std::size_t operator*() { return 0; } 
    operator++() { --number; v->push_back((*v)[4]); } 
    bool operator==(silly_iterator other) const { return number == 0; } 
    // other methods 
}; 
std::vector<std::size_t> vec = { 3, 4, 5, 6, 7 }; 
vec.insert(vec.begin() + 2, silly_iterator(&vec, 10), silly_iterator());

2 个答案:

答案 0 :(得分:1)

基本上,使vector的迭代器/引用无效的唯一原因是重新分配,因为否则你仍然指向向量的某个部分。

C ++ 11 23.3.6.3/5:

  

备注:重新分配使引用元素的所有引用,指针和迭代器无效   顺序。保证在插入之后不会发生重新分配   调用reserve()直到插入使向量的大小大于   capacity()的价值。

在插入函数C ++ 11 23.3.6.5/1:

的备注中再次重申了这一点
  

备注:如果新大小大于旧容量,则会导致重新分配。如果没有重新分配,   插入点之前的所有迭代器和引用都保持有效。 [...]

使用vector,您可以认为您的迭代器的行为与指针非常相似(这表明为什么确切的重新分配会导致问题)。实际上,reference类型是value_type&,由标准定义,表明引用确实没有被包装。

请注意,迭代器的目标可能会因插入而发生更改,因为基础数据会发生更改。此外,为了符合标准,您需要确保不会发生重新分配(例如,通过保留呼叫)。

答案 1 :(得分:1)

23.2.3表100中的C ++ 11 n3242(稍微旧草案)中,我们了解到迭代器对insert函数pre: i and j are not iterators into a。我相信基于该措辞,我会选择将其广义地解释为i and j shall not access a,并且两个迭代器都是未定义的行为。

但是,让我们说,我的广泛解释不是标准的意图。然后回答你的问题,对于输入迭代器,结果几乎肯定是:3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 5, 6, 7而前向迭代器或更好的3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 73, 4, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 5, 6, 7取决于大小是否更新之前或者将元素复制到开放空间之后。我没有看到在这种情况下指定前向迭代器的结果的任何地方。