【发布时间】:2015-12-12 03:48:30
【问题描述】:
我正在尝试使用循环和迭代器从双端队列中删除一个元素。我正在关注online examples,但发现了一个错误。
我正在使用 g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9)。
代码如下:
#include <iostream>
#include <deque>
using namespace std;
// Display the contents of a queue
void disp_deque(deque<int>& deque) {
cout << "deque contains: ";
for (auto itr = deque.begin(); itr!=deque.end(); ++itr)
cout << *itr << ' ';
cout << '\n';
}
int main(int argc, char** argv) {
deque<int> mydeque;
// Put 10 integers in the deque.
for (int i=1; i<=10; i++) mydeque.push_back(i);
disp_deque(mydeque);
auto it = mydeque.begin();
while (it!=mydeque.end()) {
cout << "Checking " << *it << ',';
// Delete even numbered values.
if ((*it % 2) == 0) {
cout << "Deleting " << *it << '\n';
mydeque.erase(it++);
disp_deque(mydeque);
} else ++it;
}
}
这很简单 - 创建一个包含 10 个元素的列表并删除偶数个。
注意以下事项(绒毛除外):
if ((*it % 2) == 0) {
mydeque.erase(it++);
} else it++;
建议使用迭代器进行删除,这样您的迭代器就不会像上面链接中提到的那样失效。
但是,当我运行它时,我得到以下信息:
$ ./test
deque contains: 1 2 3 4 5 6 7 8 9 10
Checking 1,Checking 2,Deleting 2
deque contains: 1 3 4 5 6 7 8 9 10
Checking 3,Checking 4,Deleting 4
deque contains: 1 3 5 6 7 8 9 10
Checking 5,Checking 6,Deleting 6
deque contains: 1 3 5 7 8 9 10
Checking 7,Checking 8,Deleting 8
deque contains: 1 3 5 7 9 10
Checking 10,Deleting 10
deque contains: 1 3 5 7 9
Checking 10,Deleting 10
deque contains: 1 3 5 7
Checking 0,Deleting 0
deque contains: 1 3 5
Checking 0,Deleting 0
deque contains: 1 3
Checking 0,Deleting 0
deque contains: 1
Checking 0,Deleting 0
deque contains:
Checking 0,Deleting 0
Segmentation fault (core dumped)
查看它,直到它删除 8 之前,它似乎还不错。实际上,数字 9 被完全跳过并且从未检查过!我期望发生的事情是这样的:
$ ./test
deque contains: 1 2 3 4 5 6 7 8 9 10
Checking 1,Checking 2,Deleting 2
deque contains: 1 3 4 5 6 7 8 9 10
Checking 3,Checking 4,Deleting 4
deque contains: 1 3 5 6 7 8 9 10
Checking 5,Checking 6,Deleting 6
deque contains: 1 3 5 7 8 9 10
Checking 7,Checking 8,Deleting 8
deque contains: 1 3 5 7 9 10
Checking 9,Checking 10,Deleting 10
deque contains: 1 3 5 7 9
事实上,这正是我将代码更改为这个时得到的:
if ((*it % 2) == 0) {
it=mydeque.erase(it);
} else it++;
那么,为什么一种方法有效,而另一种无效?谁能解释一下?
即使我创建了一个要删除的临时迭代器,我也会看到完全相同的问题输出:
while (it!=mydeque.end()) {
cout << "Checking " << *it << ',';
auto tmp_it = it++;
// Delete even numbered values.
if ((*tmp_it % 2) == 0) {
cout << "Deleting " << *tmp_it << '\n';
cout << "IT before delete: " << *it << '\n';
mydeque.erase(tmp_it);
cout << "IT after delete: " << *it << '\n';
disp_deque(mydeque);
}
}
在这里,我将它的副本存储在 tmp_it 中,然后将其递增。我添加了一些调试语句,并看到了一些非常奇怪的东西:
...
deque contains: 1 3 5 6 7 8 9 10
Checking 5,Checking 6,Deleting 6
IT before delete: 7
IT after delete: 7
deque contains: 1 3 5 7 8 9 10
Checking 7,Checking 8,Deleting 8
IT before delete: 9
IT after delete: 10
deque contains: 1 3 5 7 9 10
Checking 10,Deleting 10
IT before delete: 10
IT after delete: 10
...
但是,删除元素 8 使它指向元素 10,跳过了 9!在之前的删除中,它指向前一个元素(例如,当 6 被删除时,它在删除之前和之后都指向 7)。
我查看了deque 的实现,并在“迭代器有效性”下看到以下内容(强调我的):
迭代器有效性如果擦除操作包括最后一个元素 在序列中,结束迭代器和迭代器、指针和 引用已擦除元素的引用无效。如果 擦除包括第一个元素但不包括最后一个元素,只有那些 指被擦除的元素是无效的。 如果发生 双端队列中的任何其他地方,所有迭代器、指针和引用 与容器相关的无效。
这是否意味着在我的代码中,即使我在删除之前对其进行了后期增量,我的迭代器也会失效?即我删除的迭代器以外的迭代器正在失效?
如果是这样,那很好,但这似乎是一个鲜为人知的错误。这意味着common在循环中删除迭代器的实现在使用双端队列时无效。
【问题讨论】:
-
auto next_iterator = queue.erase(it):(请查阅文档)
-
您引用的示例仅适用于基于节点的容器,例如
set和list -
@TemplateRex 我开始看到了。不幸的是,使用迭代器的全部意义在于它是一种通用的循环方法,而与容器无关。太糟糕了,事实并非如此。谢谢!
-
@Trenin 是的,这很不幸,因此我在回答中引用了 Effective STL 中关于它的项目:)