std::unique仅移除连续重复元素,需先排序再调用并配合erase才能真正去重;它不保证顺序、不收缩容器、不适用于关联容器或const容器,且自定义谓词须满足等价关系。

unique 函数必须配合排序或预处理才能真正去重
std::unique 不是“自动去重”函数,它只移除**连续重复元素**的后续出现项。如果原容器未排序或未将相同元素聚拢,unique 会漏掉大量重复项。
比如 {1,3,2,3,1} 调用 unique 后仍是 {1,3,2,3,1}(无连续重复),返回的“新逻辑尾”位置等于原尾,实际没删任何元素。
- 正确做法:先调用
std::sort(或自定义分组逻辑),再用unique - 若需保持原始顺序去重(如去重但不打乱 1,3,2 的出现次序),
unique不适用,得用std::unordered_set配合遍历 -
unique返回的是新的逻辑尾迭代器,必须手动擦除后续冗余位置,例如:v.erase(std::unique(v.begin(), v.end()), v.end())
unique 返回的是迭代器,不是新容器,也不修改原大小
unique 只做“前移覆盖”,把重复元素挤到末尾,并返回第一个冗余位置的迭代器。容器物理长度不变,内存里残留的旧值仍存在——只是逻辑上被划出范围。
常见错误是调用完 unique 就以为去重完成,结果打印整个 vector 发现末尾有脏数据(比如原 {1,2,2,3} 经 unique 后变成 {1,2,3,3},但 size 还是 4)。
立即学习“C++免费学习笔记(深入)”;
- 必须配对使用
erase才能真正收缩容器:vec.erase(it, vec.end()),其中it是unique返回值 - 对
std::list可用成员函数unique()(无参数),它自动完成擦除,但前提是元素已连续重复 - 对 raw array 或 C-style string,不能直接用
unique,需转为迭代器对,且注意结尾 '\0' 处理
unique 的比较逻辑依赖 operator== 或自定义谓词,且要求稳定
unique 默认用 operator== 判断相等,但若传入自定义谓词(如 [](int a, int b) { return abs(a) == abs(b); }),必须确保该关系满足“等价类”要求:自反、对称、传递。否则行为未定义。
典型翻车场景:用浮点数近似比较写谓词,equal_to_eps(1.0, 1.0001) 和 equal_to_eps(1.0001, 1.0002) 为 true,但 equal_to_eps(1.0, 1.0002) 却为 false —— 这违反传递性,unique 可能跳过本该合并的项或崩溃。
- 整数、字符串、枚举等天然支持严格
==的类型最安全 - 自定义类型务必重载
operator==,或提供满足等价关系的谓词 - 谓词参数顺序固定:
pred(*i, *(i-1))判断当前元素是否应被移除(即是否与前一元素“等价”)
unique 对 const_iterator 无效,且不能用于关联容器
unique 要求随机访问或至少前向迭代器,并需要写权限——因为要移动元素覆盖。所以不能传 vector,也不能用于 std::set 或 std::map(它们 key 只读,且底层红黑树结构不支持原地覆盖)。
想对 set 去重?它本身就不允许重复;想从 map 值中去重?得先把 .second 拷进 vector 再处理。
- 对
std::deque和std::vector安全可用 - 对
std::forward_list需用其成员函数unique()(C++11 起),不接受谓词,仅用operator== - 对
const vector,无法调用unique,只能复制一份非常量副本再操作
erase。这两个动作缺一不可,少一个,unique 就只是个看起来在干活的哑巴函数。









