std::unordered_set插入重复元素会忽略且不报错,insert()返回pair.second为false;需重载operator==和std::hash特化;迭代器仅在重哈希时失效。

std::unordered_set 插入重复元素会怎样
它直接忽略,不报错也不覆盖——这是去重的核心机制。插入一个已存在的 key,insert() 返回 std::pair<iterator bool></iterator>,其中 second 为 false,first 指向原元素。
- 别依赖返回值做逻辑分支,除非你真需要知道“这次是不是新元素”
- 不要用
operator[](unordered_set没这玩意儿),那是unordered_map的语法 - 如果误写成
my_set[key] = value,编译直接失败,错误信息类似:no match for 'operator[]'
构造时传 vector 或 array 怎么避免重复拷贝
用初始化列表或范围构造,而不是先建空集再循环 insert。前者底层可预分配桶(bucket),后者每插一次都可能触发重哈希。
- 推荐:
std::unordered_set<int> s{v.begin(), v.end()};</int>(v是std::vector<int></int>) - 不推荐:
for (auto x : v) s.insert(x);—— 尤其当v.size()大且无序时,性能差距明显 - 如果已知大概元素数,提前
reserve(n)能减少重哈希次数,但注意:这是对桶数量的预留,不是对元素数
自定义类型用 unordered_set 必须重载什么
两个东西缺一不可:operator== 和一个合法的哈希函数(通常特化 std::hash)。只重 == 会编译失败;只提供哈希不重 == 可能导致逻辑错误(比如两个不同对象哈希相同但 == 返回 false,它们仍被当作不同元素)。
- 哈希函数必须满足:若
a == b,则hash(a) == hash(b);反之不成立 - 别在哈希函数里调用
std::hash以外的非constexpr函数(如std::to_string),否则无法用于模板实例化 - 常见翻车点:结构体字段含指针或
std::string_view未处理生命周期,哈希值随内存变化而变
迭代器失效的边界情况
unordered_set 迭代器只在**重哈希**时整体失效(比如 insert 触发扩容),单次 erase 不会让其他迭代器失效——这点和 vector 完全不同,但容易被经验误导。
立即学习“C++免费学习笔记(深入)”;
- 安全写法:
for (auto it = s.begin(); it != s.end(); ) { if (cond(*it)) it = s.erase(it); else ++it; } - 危险写法:
for (auto it : s) { if (cond(it)) s.erase(it); }—— 迭代器it在erase后立即失效,下一轮循环就 UB - 插入过程中若触发重哈希,所有现存迭代器、引用、指针全部失效;所以别一边遍历一边无条件插入
unordered_set 当作有序容器来依赖遍历顺序。











