使用iterator和const_iterator调用erase

时间:2011-02-03 11:03:04

标签: c++ data-structures

为什么调用erase容器的const_iterator成员函数会失败?

它适用于非const iterator

5 个答案:

答案 0 :(得分:26)

这不会编译,因为container::iteratorcontainer::const_iterator是两种不同的类型,且擦除的唯一(单参数)版本是:iterator erase(iterator);

不接受const_iterator可被视为语言标准中的缺陷:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2350.pdf

这种限制没有特别的原因。迭代器仅用于在(可修改的)容器中指示位置,并且在inserterase的情况下都不是修改的迭代器的“指针”(在在erase的情况下它只是在概念上不存在,这对于const对象来说是正常的事情。)

当前标准表示“迭代器常量和容器常量”之间的混淆(这里也是其他答案),并且似乎const_iterator可能在C ++ 0x中被erase接受。


作为一种变通方法,您可以有效地从iterator获取const_iterator,因为容器首先必须是可变的。

下面的函数只适用于随机访问迭代器,因为使用其他类型的迭代器执行此操作可能有点太慢了。

#include <vector>

template <class Container>
typename Container::iterator to_mutable_iterator(Container& c, typename Container::const_iterator it)
{
    return c.begin() + (it - c.begin());
}

int main()
{
    int arr[] = {1, 5, 2, 5, 3, 4, 5, 1};
    std::vector<int> vec(arr, arr + sizeof(arr) / sizeof(*arr));
    for (std::vector<int>::const_iterator it = vec.begin(); it != vec.end(); ) {
        //if (*it = 5) {  //const_iterator prevents this error
        if (*it == 5) {
            it = vec.erase(to_mutable_iterator(vec, it));
        }
        else {
            ++it;
        }
    }
}

但是,重新构建代码可能会更好,因此您首先不需要const_iterator。在这种情况下,最好使用std::remove算法。如果你需要在擦除之前做更多的非变异工作,你可以将它提取到一个单独的方法等。

答案 1 :(得分:5)

我只想强调UncleBens,David Rodriguez和Ise Westeria发布的答案/评论的一般正确性。

无论当前(或以前)的C ++ 11之前的编译器的行为如何,const_iterator(应该)的const正确性会立即停止,因为它在语义上等于const T *(或T * const) - 请注意T本身可能是它自己的const类型! - 因此它有效地防止代码修改容器中引用的对象。

然而,因为在C ++中“删除”一个const指针是完全合法的(尝试它,它可以工作!),它应该是(并且行为已在C ++ 11中得到纠正)合法以及'擦除“来自容器的const迭代器,只要容器本身不是const。

似乎Visual Studio 2010已经通过“擦除”接受const_iterator来正确运行,这当然让我有些头疼来追踪其他一些bug,这导致我发表这篇文章,最终阐明了“擦除”的正确行为const_iterator“const correctness。

答案 2 :(得分:3)

关于constness,您可以将std::container<T>::const_iterator视为const T*

答案 3 :(得分:2)

类型const_iterator不能用于修改元素或容器的值。

答案 4 :(得分:-1)

这就是const_iterator的目的。当通过它访问的元素不应该被修改时,你使用它们。