循环在 std::list 上为迭代的 std::erase 挂起
Posted
技术标签:
【中文标题】循环在 std::list 上为迭代的 std::erase 挂起【英文标题】:loop hangs for iterated std::erase on std::list 【发布时间】:2020-01-30 23:32:30 【问题描述】:我正在尝试使用哈希表删除存储在列表中的整数向量的重复组合。遍历列表中的每个整数向量,I:
-
计算 hash_value (thash)
查看哈希值是否已经在哈希表(pids)中
如果它在哈希表中,请从列表中删除该向量。
否则,将该值添加到 hash_table 并增加列表
迭代器
打印语句似乎证实了我的逻辑,但循环在迭代的第四步挂起。我已经评论了导致问题的it++
和vz.remove(it)
,并且只在下面的代码中显示了逻辑。代码也可以通过ideone获得:https://ideone.com/JLGA0f
#include<iostream>
#include<vector>
#include<list>
#include<cmath>
#include<unordered_set>
using namespace std;
double hash_cz(std::vector<int> &cz, std::vector<double> &lprimes)
double pid = 0;
for(auto it = cz.begin(); it != cz.end(); it++)
pid += lprimes[*it];
return pid;
int main()
// create list of vectors
std::list<std::vector<int>> vz;
vz.push_back(2,1);
vz.push_back(1,2);
vz.push_back(1,3);
vz.push_back(1,2,3);
vz.push_back(2, 1);
// vector of log of prime numbers
std::vector<double> lprimes 2, 3, 5, 7;
for (auto it = lprimes.begin(); it != lprimes.end(); it++)
*it = std::log(*it);
std::unordered_set<double> pids;
double thash;
for (auto it = vz.begin(); it != vz.end(); )
thash = hash_cz(*it, lprimes);
std::cout << thash << std::endl;
// delete element if its already been seen
if (pids.find(thash) != pids.end())
std::cout << "already present. should remove from list" << std::endl;
// vz.erase(it);
else
// otherwise add it to hash_table and increment pointer
std::cout << "not present. add to hash. keep in list." << std::endl;
pids.insert(thash);
// it++;
it++;
for (auto it = vz.begin(); it != vz.end(); it++)
for (auto j = it -> begin(); j != it -> end(); j++)
std::cout << *j << ' ';
std::cout << std::endl;
return 0;
【问题讨论】:
【参考方案1】:问题是这行代码:
vz.erase(it);
它将迭代器保持在原来的位置,即使其无效。它应该是:
vz.erase(it++);
或
it = vz.erase( it );
注意:std::unoredered_set::insert()
返回值告诉你插入是否成功(如果相同的值元素已经存在),你应该调用它并检查结果。在您的代码中,您会进行两次查找:
if (pids.insert(thash).second )
// new element added
++it;
else
// insertion failed, remove
it = vz.erase( it );
由于std::list
提供remove_if()
,您的代码可以简化:
vz.remove_if( [&pids,&lprimes]( auto &v )
return !pids.insert( hash_cz(v, lprimes) ).second );
);
而不是整个循环。
【讨论】:
优秀的答案。非常感谢你的帮助。并感谢有关插入unordered_sets
的提示。【参考方案2】:
如果元素已经被看到,你擦除()it
节点,然后在循环结束时增加it
:未定义的行为。改用erase(it++)。
如果尚未看到该元素,则递增it
,然后在for
的末尾再次执行此操作,如果it
是end() - 1
,则在它移过末尾时产生UB。
【讨论】:
如此简单。感谢您的澄清!以上是关于循环在 std::list 上为迭代的 std::erase 挂起的主要内容,如果未能解决你的问题,请参考以下文章
在基于范围的 for 循环期间插入 std::list 的后面
如何使用 OpenMP 通过 C++ std::list 并行化 for 循环?