2012-02-11 66 views
2

我尝试下面的代码擦除外国迭代

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

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

+0

@zerOne:查看我更新的答案。 – Nawaz 2012-02-12 03:43:04

+0

谢谢。当我得到更多积分时,我会回来投票。 – zer0ne 2012-02-13 23:15:12

回答

3

这调用不确定的行为,因为你传递从一个容器取得迭代其他容器的功能。 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,你应该在大多数情况下使用它。这是C++默认容器类型,只有在您充分的理由这样做,你应该使用std::list

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所有元素,那么你可以申请受欢迎Erase-Remove Idiom为:

a.erase(std::remove(a.begin(), a.end(), value), a.end()); 

注意std::vector没有remove()成员函数,这就是为什么你申请这个成语。你可以阅读my answer here,更详细地讨论这个问题。

+0

那么如何测试迭代器是否在容器的范围内。 在这个例子中,b.begin()!= a.end()并不意味着b.begin()是一个有效的迭代器。 – zer0ne 2012-02-11 17:29:51

+0

@ zer0ne:我想,你无法知道(可靠地)。你需要以这样的方式编程,以至于你永远不会需要它。 – Nawaz 2012-02-11 17:31:40

1

这简直是不确定的行为,所以任何事情都有可能发生,任何你观察的是在某些方面的“预期行为”。

不要这样做。

std::list::erase的先决条件很明显,参数是容器的元素的迭代器

0

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

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()从迭代器获得的指针链表节点并执行类似上面的代码。如果您为迭代器启用调试支持,则此代码可能会导致运行时断言。

0

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

相关问题