为什么vector :: pop_back使迭代器无效(end() - 1)?

时间:2013-07-22 01:49:10

标签: c++ iterator invalidation

注意:该问题也适用于erase。见底部。


end() - 1上调用pop_back之后vector迭代器失效的原因是什么?

澄清一下,我指的是这种情况:

std::vector<int> v;
v.push_back(1);
v.push_back(2);

std::vector<int>::iterator i1 = v.begin(), i2 = v.end() - 1, i3 = v.begin() + 1;

v.pop_back();

// i1 is still valid
// i2 is now invalid
// i3 is now invalid too

std::vector<int>::iterator i4 = v.end();

assert(i2 == i4);  // undefined behavior (but why should it be?!)
assert(i3 == i4);  // undefined behavior (but why should it be?!)

为什么会这样? (即这种失效何时会证明对实施有益?)

(请注意,这不仅仅是一个理论问题。如果您尝试在调试模式下执行此操作,如果您有_ITERATOR_DEBUG_LEVEL,那么Visual C ++ 2013 - 也可能是2012年 - 会显示错误设置为2。)


关于erase

请注意,同样的问题适用于erase
为什么erase(end() - 1, end())无效end() - 1

(所以请不要说,pop_back会使end() - 1无效,因为它等同于调用erase(end() - 1, end());这只是提出问题。)

2 个答案:

答案 0 :(得分:2)

有趣的问题是,迭代器无效是什么意思。我真的没有从标准中得到很好的答案。我所知道的是,在某种程度上,标准认为迭代器不是指向容器内部位置的指针,而是作为容器内特定元素的代理。

考虑到这一点,在删除向量中间的单个元素之后,删除点之后的所有迭代器都变为无效,因为它们不再引用它们引用的相同元素之前。

支持这种推理来自容器中其他操作的迭代器失效子句。例如,在insert上,标准保证如果在之前没有重新分配迭代器,则插入点仍然有效。 Exibtio probat regulam in casibus non exceptis ,它会在插入点之后使所有迭代器无效。

如果迭代器的有效性仅与迭代器指向的容器元素这一事实有关,那么迭代器的 none 将因该操作而失效(同样,在没有重新分配)。

在该推理线中更进一步,如果将迭代器有效性视为指针有效性,则在erase操作期间,向量中的所有迭代器都不会失效。 end()-1迭代器将变为不可解除引用,但它可以保持有效,但事实并非如此。

答案 1 :(得分:0)

pop_back通常根据erase(end()-1, end())定义。

从向量中擦除一系列迭代器时,第一个擦除和转发的所有迭代器都将失效。这包括一个范围。

通常,有效的derefencable非输入迭代器始终引用相同的数据“位置”,直到它变为无效。在erase的情况下,之后的所有非无效迭代器当前具有相同的值位置。

必须修改上述两条规则才能获得所需的行为。第一个类似'除非你擦除这样做的最后一个元素,在这种情况下,第一个擦除元素成为一个过去的结束迭代器'。并且仍然有效的迭代器将不会被解除引用,对于迭代器而言可能是唯一的状态更改,据我所知,在C ++中没有先例。

在标准中,成本既是额外棘手的问题,也可以覆盖您所请求的行为,并且对严格的迭代器进行健全性检查。好处 - 好吧,我没有看到一个:在每种情况下你都必须确切地知道刚刚发生了什么(一个非常特殊的迭代器刚刚结束而不是失效),如果你知道那就是你的情况可以谈谈end

并且需要单词。当您调用erase( a, b )时,来自a的每个迭代器都将以某种方式更改其状态(*a将不返回相同的值,以及必须如何指定更改)。 C ++采用简单的方法,并简单地声明状态由erase更改的每个迭代器都变为无效,并且使用它是未定义的行为:这允许实现者的最大纬度。

理论上,它也可以允许优化。如果在erase操作之前和之后取消引用迭代器,则可以认为您获得的值相同! (假设在对象析构函数中没有可能的间接修改容器,这也可以证明)。