std::unique仅移除相邻重复元素,返回新逻辑结尾迭代器,需配合erase缩容;不排序则无法全量去重;支持自定义谓词,但须满足等价关系。

std::unique只能去重相邻重复,不是全量去重
std::unique 不是“删除所有重复项”,它只移除**连续重复**的元素,把重复块压缩成一个,并返回新逻辑结尾的迭代器。原容器大小不变,多余元素留在末尾(值未定义,但通常保留原值)。想真正缩短容器,得配合erase用。
常见错误是直接调用std::unique(v.begin(), v.end())却不接返回值,或没做erase,结果看似没变化。
- 必须先保证待处理区间已排序(若要全量去重),否则
std::unique只会吃掉像[1,1,2,2,1]中前两个1和前两个2,留下末尾的1 - 它不比较“值是否全局存在过”,只看
*it == *(it-1) - 对
vector、string等支持随机访问的容器最常用;对list有成员函数unique(),更高效(不用移动元素)
正确用法:必须配合erase才能缩容
标准写法是“erase–remove 惯用法”的变体:erase(unique(...), end())。注意不是 remove,unique 是专用相邻去重。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::vector<int> v = {1, 1, 2, 2, 2, 3, 3, 1};
auto last = std::unique(v.begin(), v.end()); // last 指向第1个冗余位置:v[7](原值1)
v.erase(last, v.end()); // 真正删掉 [last, end)
// v 变为 {1, 2, 3, 1}
-
std::unique返回的是新逻辑结尾,不是被删元素个数 - 不能对
const容器或只读区间调用 - 如果用在
std::string上,一样生效:s.erase(std::unique(s.begin(), s.end()), s.end())
自定义比较逻辑:用谓词控制“重复”定义
默认用 operator==,但可通过第3个参数传入二元谓词。比如忽略大小写去重字符串,或按绝对值去重整数。
立即学习“C++免费学习笔记(深入)”;
std::vector<int> v = {-1, 1, -2, 2, 2, -2};
auto last = std::unique(v.begin(), v.end(), [](int a, int b) {
return std::abs(a) == std::abs(b); // 视 -1 和 1 为重复
});
v.erase(last, v.end()); // 得到 {-1, -2}
- 谓词接收的是“当前元素”和“前一元素”,顺序固定:
pred(*(it-1), *it) - 谓词必须满足等价关系(自反、对称、传递),否则行为未定义
- 不能用捕获 lambda 捕获局部变量后传给
unique——除非用std::function包装(有开销),一般推荐无捕获 lambda 或普通函数
性能与边界注意点
std::unique 是单趟遍历,时间复杂度 O(n),不分配内存,但会移动后续元素(对 vector 是拷贝/移动赋值)。对大对象,移动成本不可忽视。
- 对
std::list,优先用lst.unique()成员函数——它直接解链,不移动节点值 - 空区间或单元素区间安全调用,返回
begin()或begin()+1 - 对
std::forward_list,没有unique成员函数,也不能用std::unique(因不支持双向迭代),需手写循环 - 如果原数据来自文件或网络流,且只需输出去重结果,别急着塞进 vector 再 unique——考虑边读边判重,省空间
erase,或者误以为它能替代 std::set 做全局去重。邻近性这个前提,得刻在调用前的脑子里。

















