std::unique对{1,2,1,2}无反应是因为它仅比较相邻元素,而该序列中无相邻相等元素;其设计本就不适用于无序数据去重,需配合sort和erase使用。

std::unique 为什么对 {1,2,1,2} 完全没反应
因为 std::unique 只比较相邻元素:它从第二个元素开始,逐个判断 *it == *(it-1) 是否成立。若不成立,就保留;若成立,就跳过(标记为待移除)。{1,2,1,2} 中没有任何一对相邻元素相等,所以 std::unique 直接返回原 end() 迭代器,容器内容看起来“完全没变”。
这不是 bug,是设计如此——它本就不是为无序数据准备的去重工具。
- 常见错误现象:调用后打印整个
vector,发现重复值还在,甚至末尾出现脏数据(如 {1,2,1,2,2} → 实际变成 {1,2,1,2,2},但逻辑尾仍是end()) - 别试“手动把相同值挪到一起”这种土办法,效率低还易错;排序是标准、可控、可复用的前置手段
- 若原始顺序必须保留(比如日志时间序列),
std::unique就不该用,换std::unordered_set配合遍历
std::sort + std::unique + erase 缺一不可
三步各自干的事不能混:std::sort 聚类,std::unique 标记,erase 物理删。漏掉任何一步,结果都不可靠。
-
std::sort后未调std::unique:数据有序但仍有重复 - 只调
std::unique不erase:容器size()不变,末尾残留旧值;用for (auto x : v)会遍历到这些“幽灵元素” - 用错
erase范围,比如写成v.erase(v.begin() + 1, v.end()):删掉的是位置而非逻辑尾,必然误删 - 正确写法只有一种惯用模式:
v.erase(std::unique(v.begin(), v.end()), v.end())
自定义类型去重时 operator== 和 sort 比较逻辑必须一致
比如按 id 去重,std::sort 用 operator 或 lambda 比较 <code>id,std::unique 就得用对应谓词判断 id == id。两者语义错位,结果就不可预测。
立即学习“C++免费学习笔记(深入)”;
- 编译失败常见于:自定义结构体没定义
operator==,又没传谓词,std::unique找不到比较方式 - 运行时出错常见于:谓词写成
a.id > b.id或abs(a) ,和 <code>sort的不匹配 - 浮点数慎用谓词:近似相等不满足传递性,
std::unique可能跳过本该合并的项 - 安全做法:基础类型直接用默认行为;自定义类型显式重载
operator==和operator,且确保逻辑同源
std::list::unique 成员函数是个例外
std::list 提供了成员函数 unique(),它自动完成“相邻去重 + 内存擦除”,不需要手配 erase。但它依然要求元素已相邻重复——也就是说,你还是得先 sort(或用 list::sort)。
- 不能省略排序:对未排序
list直接调l.unique(),效果和vector上的std::unique一样——只去连续段 - 成员函数不接受谓词重载(C++11 起支持),但用法仍是
l.unique([](const auto& a, const auto& b) { return a.id == b.id; }) - 别在
vector上幻想有类似捷径:没有vector::unique,硬写等于自己实现一遍移动逻辑
std::unique 返回的是迭代器,不是新容器”这件事。很多人以为它像 Python 的 set() 一样生成结果,结果在调试器里看到 size 没变、末尾数据乱码,才意识到自己漏了 erase。记住:它只负责“划界”,擦不擦,得你动手。


















