5

根据 C++ 规范(23.2.4.3),vector::erase() 仅使“擦除点之后的所有迭代器和引用”无效

因此,当使用 reverse_iterators 传递所有向量成员时,当前迭代器上的擦除不应导致rend() 成员无效。

此代码将在 G++ 下运行,但会在 Windows (VS2010) 上提供运行时异常:

#include <vector>

using namespace std;

int main()
{
    vector<int> x;
    x.push_back(1);
    x.push_back(2);
    x.push_back(3);

    //Print
    for(vector<int>::const_iterator i = x.begin(); i != x.end(); ++i)
        printf("%d\n", *i);

    //Delete second node
    for(vector<int>::reverse_iterator r = x.rbegin(); r != x.rend(); ++r)
        if(*r == 2)
            x.erase((r+1).base());

    //Print
    for(vector<int>::const_iterator i = x.begin(); i != x.end(); ++i)
        printf("%d\n", *i);

    return 0;
}

这个错误很有趣:

表达式:向量迭代器不可递减

在第二次运行时在第二个 for 循环的行上给出。减量指的是 reverse_iterator 的内部“当前”迭代器成员,每当 reverse_iterator 增加时,它就会减少。

任何人都可以解释这种行为吗?

谢谢。

编辑

我认为这个代码示例更好地表明这不是 r 的问题,而是 rend() 的问题:

//Delete second node
for(vector<int>::reverse_iterator r = x.rbegin(); r != x.rend();)
{
    vector<int>::reverse_iterator temp = r++;

    if(*temp == 2)
        x.erase((temp+1).base());
}

vector iterators incompatible并在擦除后进入 for 循环时出错。

4

5 回答 5

4

您的程序调用未定义的行为。因此,两个编译器都不正确。

根据标准,std::vector<int>::reverse_iteratorstd::reverse_iterator<std::vector<int>::iterator>. 的实现std::reverse_iterator<Iter>被指定为有一个protected成员Iter current;,所有其他成员和函数reverse_iterator都是基于该成员的行为来指定的current

所以假设我们有r == reverse_iterator(i), wherei是一个有效的迭代器std::vector<int> x;。然后,这些中的每一个都由标准保证。

r.current == i
(r+1).current == (i-1)
(r+1).base() == (i-1)

在调用x.erase((r+1).base());之后,所有迭代器i-1都将失效。当然,这包括i,因此也包括r.current

您的程序尝试评估的下一件事是++r. 此表达式被指定为具有效果--r.current;。但是既然r.current被无效化了,这个表达式就是Undefined Behavior;也是如此++r

于 2011-03-08T18:32:54.027 回答
3

Areverse_iterator通常只是一个普通迭代器的包装器,因此递增反向迭代器可能会在下面递减一个。类似地,rbegin将返回一个迭代器,该迭代器在容器中的所有元素之后,因此它会以同样的方式失效。

于 2011-03-08T17:34:49.217 回答
2

这是一种更好的方法来做你想做的事情:

struct CustomRemove
{
    bool operator()(int i)
    {
        return (i == 2);
    }
};

int main()
{
    std::vector<int> x;
    x.push_back(1);
    x.push_back(2);
    x.push_back(3);

    CustomRemove custom_remove;

    std::copy(x.begin(), x.end(), std::ostream_iterator<int>(std::cout, "\n"));
    x.erase(std::remove_if(x.begin(), x.end(), custom_remove), x.end());
    std::copy(x.begin(), x.end(), std::ostream_iterator<int>(std::cout, "\n"));

    return 0;
}
于 2011-03-08T17:58:18.767 回答
1

A reverse_iterator internally stores a normal iterator to the position after its current position. It has to do that, because rend() would otherwise have to return something before begin(), which isn't possible. So you end up accidentally invalidating your base() iterator.

于 2011-03-08T17:37:47.770 回答
0

由于(r+1).base()并且r有效地指向同一个元素,因此擦除(r+1).base()实际上确实使 无效r。很可能 g++ 只是在引擎盖下使用了一个指针,所以它看起来都可以正常工作。

如果您尝试从向量中删除匹配元素,则更好的方法是使用removeor remove_if

于 2011-03-08T18:01:45.900 回答