擦除外部迭代器

时间:2012-02-11 17:11:41

标签: c++ iterator erase

我试验了以下代码

list<int> a ={1,2};
list<int> b ={3,4};
a.erase(b.begin());
for (auto x:b) cout << x << endl;

奇怪的是,该程序运行良好,没有任何错误。打印出来的是4.我想知道当对象已经隐含在迭代器中时,为什么 erase 是一个成员函数。

5 个答案:

答案 0 :(得分:3)

a.erase(b.begin());

这会调用未定义的行为,因为您将从一个容器获取的迭代器传递给其他容器的函数。 ab是两个不同的容器,它们不相同。

未定义的行为意味着可能发生任何事情:可能按预期运行,或者可能不会。语言规范和编译器都不保证它能够正常工作。恰当地说“未定义的行为”。

你应该做的是:

auto value = *(b.begin()); //value is int
auto it = std::find(a.begin(), a.end(), value); //returns iterator 
if ( it != a.end())
   a.erase(it); //well-defined, as the iterator belongs to the same container!

或者,如果您要删除所有等于value的元素,那么您可以这样做:

a.remove(value); //std::list has remove member function

但是,如果您在大多数情况下使用std::vector,那么您应该使用std::list。它是C ++中的默认容器类型,只有在您有充分理由这样做时才应使用std::vector<int> a ={1,2}; std::vector<int> b ={3,4}; //if you want to remove one element: auto value = *(b.begin()); //value is int auto it = std::find(a.begin(), a.end(), value); //returns iterator if ( it != a.end()) a.erase(it); //well-defined, as the iterator belongs to the same container!

value

如果您要删除所有等同于a.erase(std::remove(a.begin(), a.end(), value), a.end()); 的元素,那么您可以将受欢迎的Erase-Remove Idiom应用为:

std::vector

请注意,remove()没有{{1}}成员函数,这就是您应用此习惯用语的原因。您可以阅读my answer here,其中详细讨论了这一点。

答案 1 :(得分:1)

这只是未定义的行为,因此任何事情都可能发生,并且您观察到的任何在某种程度上是&#34;预期的行为&#34;。

不要这样做。

std::list::erase的前提条件显然是参数是容器元素的迭代器

答案 2 :(得分:0)

C ++是一个标准,它不需要迭代器知道它属于哪个容器。我们无法更改标准只是因为在一个特定的实现中,函数可以在不需要特定参数的情况下完成其工作。

答案 3 :(得分:0)

其他人已经提到,根据C ++标准,这会导致未定义的行为。

然而,双链表的优点在于从列表中删除节点只需要指向该节点的指针,不需要引用容器本身,例如:

template<class Tag>
inline void unlink(ListNode<Tag>* node)
{
    ListNode<Tag> *prev = node->prev_, *next = node->next_;
    prev->next_ = next;
    next->prev_ = prev;
    node->prev_ = node;
    node->next_ = node;
}

您的代码恰好正常工作,因为std::list<>通常实现为双链表,list<>::erase()从迭代器获取指向列表节点的指针,并执行类似于上面的代码。如果您为迭代器启用了调试支持,则此代码可能会导致运行时断言。

答案 4 :(得分:0)

如果std::list::erase实际上不需要了解this,那么这只是一个实施细节。它是与其他容器保持一致的成员函数。实际上,您可以将容器作为模板参数并调用container.erase(iter);