为什么erase不支持反向迭代器?

11

我刚刚写了下面这段代码,很惊讶它没有编译通过:

std::deque<int> container;
// filling the container...
for (auto it = container.rbegin(); it != container.rend(); ++it)
    if (*it == 5)
    {
        container.erase(it);
        break;
    }

如您所见,我想删除与特定条件匹配的最后一个元素(如果有的话)。

错误是:

no matching function for call to std::deque::erase(std::reverse_iterator...

起初我不相信这是由反向迭代器引起的,但实际上情况确实如此,因为用begin/end替换rbegin/rend可以解决问题。

所以,两个问题:

  1. 为什么不支持这种方式?这只是C++委员会忘记在标准中包含的小事之一,还是缺少这种重载的正当理由?
  2. 实现我想要的最优雅的方法是什么?我必须使用索引进行迭代吗?

4
我认为您的答案在这里:https://dev59.com/ZHI-5IYBdhLWcg3wfoW1。 - user325117
@MartinBroadhurst:在您的建议下进行了谷歌搜索,我找到了这篇文章,它提供了一些见解,但仍然没有解释为什么reverse_iterator::base指向它所指向的位置:(: http://www.drdobbs.com/cpp/three-guidelines-for-effective-iterator/184401406?pgno=3 - Violet Giraffe
1
@Violet - 这是因为rend()无法返回一个指向begin之前的迭代器,因为这样的迭代器并不存在。所以它会持有begin()并通过调整所有访问来进行补偿。 - Bo Persson
1
@VioletGiraffe 你说得对,我是个白痴。不过,为了尽可能多地使用<algorithm>,我建议使用erasefind_if(container.rbegin()...)的结果(即使它并不能减少代码行数)。 - screwnut
1
@screwnut 不,我在争论你坚持让OP从清晰易懂的代码转换为更加晦涩难懂的代码,而这样做没有任何好处。你的第一个建议甚至是一种悲观主义。这有什么意义?“尽可能使用<算法>”的精神是错误的。盲目模仿编程=不好。 - Asteroids With Wings
显示剩余10条评论
2个回答

1
我觉得在标准库中需要添加大量的额外重载,这使得这个任务显得不合适,而且也没有“必要”。
当然,“最优雅”的解决方法几乎没有表现力:
container.erase((it+1).base());

...但是确实存在一个问题,反向迭代器可能不常用,因此在每个地方都添加对上述代码行进行包装的重载函数会带来很多额外的噪音。

如果您发现自己经常使用它,可以自己封装更通用的解决方案:

template <typename ReverseIterator>
auto AsForwardIterator(ReverseIterator rit)
{
    std::advance(rit, 1);
    return rit.base();
}

然后就是这样:

container.erase(AsForwardIterator(it));

而且,坦率地说,这种简单性似乎值得在每个容器中不使用所有那些过载函数。

0

我无法回答“为什么”的问题,但是为了回答“如何” - 您应该在迭代器上调用base()。它将返回一个适当的前向迭代器。

在这样做时,请记住反向和前向迭代器之间的关系。起初可能会令人困惑,但实际上非常简单。如果您有一个包含以下项目的std::vector

1, 2, 3, 4, 5

假设你有一个reverse_iterator rit,当对其进行解引用时会得到3,那么*(rit.base)将等于4。要理解这一点,只需记住在普通迭代器中,begin()是可解引用的,但end()不是。在反向迭代器中,属性必须相同-rbegin()必须是可解引用的,但rend()不应该是可解引用的,即应该指向容器开头之外的位置。

由于根据定义,rend.base()begin()相同(因为可以将rend构造为reverse_iterator(begin())),所以上述所有内容都成立的唯一方法是,rend.base()将返回一个比开头之后的下一个元素更右侧的元素。很容易看出,rend()也具有相同的对称性。


因为这样会删除错误的元素!迭代器必须先调整1个。 - Violet Giraffe
@VioletGiraffe,定义错误。它会删除你的反向迭代器当前指向的元素。 - SergeyA
不,查看建议答案。 - Violet Giraffe
@VioletGiraffe,是的,你需要将它推进,这很明显,因为rbegin不可解引用,但begin可以。我没有给你代码,只是一种技巧。尽管如此,你是你自己的downvotes所有者,可以随意使用它们。) - SergeyA
@VioletGiraffe,收到。我尝试通过一些解释来完善答案。使用反向迭代器实际上非常容易,它们并没有什么神秘的魔力。 - SergeyA
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接