【问题标题】:How does the Standard deal with self-referencing iterators in container insert functions?标准如何处理容器插入函数中的自引用迭代器?
【发布时间】:2013-05-12 14:41:37
【问题描述】:

在处理像 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());

【问题讨论】:

  • 标准没有禁止这样的事情,它只是说明了哪些迭代器何时失效。如果您的一个输入迭代器在插入过程中碰巧失效,那真是太可惜了,这么好的vector 被砸了...
  • @n.m.:但它必须定义何时它们无效。
  • 我现在没有可用的标准。我会假设它们在插入之前或期间的某个时间点失效。
  • 我在stackoverflow.com/questions/10218223/… 有一个类似的问题。我认为这是未定义的行为,但有迹象表明这是 Microsoft VS 2010 特有的错误。
  • @MarkRansom:这是完全不同的,因为标准定义了您编写的内容应该可以工作,而我的问题是标准为此代码定义的内容(并且两个不同功能的规范非常不同)。

标签: c++


【解决方案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 取决于大小是在复制元素之前还是之后更新进入空旷的空间。在这种情况下,我看不到任何地方会指定前向迭代器的结果。

【讨论】:

    【解决方案2】:

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

    C++11 23.3.6.3/5:

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

    这在插入函数 C++11 23.3.6.5/1 的注释中再次重申:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多