6

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


在调用 aend() - 1之后迭代器无效的原因是什么?pop_backvector

为了澄清,我指的是这种情况:

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?!)

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

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


关于erase

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

(所以请不要说,pop_back无效end() - 1,因为它等同于调用erase(end() - 1, end());那只是在乞求问题。)

4

2 回答 2

2

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

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

对这种推理的支持来自容器中其他操作的迭代器失效子句。例如, on insert,标准保证如果没有重新分配,则插入点之前的迭代器保持有效。casibus non exceptionis 中的例外概率规则,它使插入点之后的所有迭代器无效。

如果迭代器的有效性仅与迭代器指向的容器元素有关,那么任何迭代器都不会因该操作而失效(同样,在没有重新分配的情况下)。

更进一步地推理,如果您将迭代器有效性视为指针有效性,那么在操作期间没有一个迭代器会失效erase。迭代器将end()-1变得不可解引用,但它可以保持有效,但事实并非如此。

于 2013-07-24T03:24:32.530 回答
0

pop_back通常用 来定义erase(end()-1, end())

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

一般来说,一个有效的可取消引用的非输入迭代器总是引用相同的数据“位置”,直到它失效。在 的情况下erase,之后的所有非无效迭代器当前具有相同的值位置。

必须修改上述两条规则以获得您想要的行为。第一个类似于“除非您这样做擦除最后一个元素,在这种情况下,第一个擦除的元素将成为一个过去的迭代器”。并且仍然有效的迭代器将变得不可取消引用,这可能是迭代器的唯一状态更改,据我所知,这在 C++ 中是没有先例的。

成本既是标准中用于涵盖您请求的行为的额外棘手的措辞,也是对严格迭代器的健全性检查。好处 - 好吧,我没有看到:在每种情况下,您都必须确切地知道刚刚发生了什么(一个非常特殊的迭代器只是在结束后变成了一个,而不是被无效),如果您知道是这种情况,您只能说说end

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

理论上,它也可以进行优化。如果你在一个操作之前和之后取消引用一个迭代器erase,你得到的值可以被认为是相同的!(假设在对象析构函数中不可能间接修改容器,这也可以证明)。

于 2013-07-22T03:47:10.437 回答