std::unique不能直接删除重复元素,因为它只将连续重复元素移至末尾并返回新逻辑结尾迭代器,容器大小不变,需配合erase才能真正删除。

std::unique 为什么不能直接删除重复元素
std::unique 并不真正删除任何元素,它只是把重复的连续相同元素“挤到末尾”,并返回一个指向新逻辑结尾的迭代器。原容器大小不变,被“挤走”的元素仍留在内存里,只是位置变了。如果你不配合 erase,调用完 std::unique 后打印容器,会看到后面一堆脏数据或旧值。
正确配对 erase 的写法(vector 常见场景)
必须用 erase 删除 unique 返回的迭代器到 end() 这一段。常见错误是漏掉 vec.erase(...),或者误写成 vec.erase(unique(...), vec.end()) 却没接住返回值——unique 返回的是新尾迭代器,不是 void。
- 先确保容器已排序(若要去重所有重复项),因为
std::unique只处理**连续重复** - 正确链式写法:
vec.erase(std::unique(vec.begin(), vec.end()), vec.end()) - 若用在
std::list,可直接调用list.unique()成员函数,无需手动 erase - 对
std::vector<:string>等类型,unique比较基于operator==,自定义类需提供该运算符
去重前没排序导致结果不符合预期
比如 vector,直接 unique + erase 得到的是 {1,2,1,3,2} → {1,2,1,3,2}(只删了连续重复,这里没有),最终仍是原样。这不是 bug,是设计使然。
- 要全局去重:先
std::sort(v.begin(), v.end()),再unique + erase - 想保持原始顺序去重?
std::unique不适用,得用std::unordered_set辅助遍历保留首次出现项 - 性能注意:排序是 O(n log n),
unique是 O(n),合起来比哈希去重(O(n) 平均)慢,但省空间
lambda 自定义比较时的陷阱
传 lambda 给 std::unique 时,必须捕获为空([ ] 或 [=] 都不行),因为 unique 内部可能复制该 callable;而带状态的 lambda 无法满足 CopyConstructible 要求,编译失败。
- 安全写法:用函数对象(struct +
operator())或普通函数指针 - 例如按绝对值去重:
std::unique(v.begin(), v.end(), [](int a, int b) { return std::abs(a) == std::abs(b); })—— 这个 lambda 是无状态的,合法 - 但
[x](int a, int b) { return a == b + x; }会编译报错:not copy constructible
实际去重逻辑是否成立,取决于你传的比较谓词是否满足等价关系(自反、对称、传递)。不小心写错谓词,unique 行为就不可预测——这点容易被忽略,调试时很难定位。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











