std::unique仅压缩连续重复元素并返回新逻辑结尾迭代器,不改变容器大小,必须配合erase才能真正删除冗余元素;常见错误包括遗漏erase、范围用错或未排序直接使用。

std::unique 本身不删除元素,只是重排
std::unique 不会真正删掉容器里的重复元素,它只把**连续重复的元素压缩成一个**,并返回指向新逻辑结尾的迭代器。原容器大小不变,多余元素留在末尾(值未定义,但通常保持原值)。所以必须手动用 erase 配合它“真正删掉”那段冗余内存。
正确配对:erase + unique 的经典写法
最常用、最安全的组合是 erase 接 unique 返回的迭代器范围:
std::vector<int> v = {1, 2, 2, 3, 3, 3, 4};
v.erase(std::unique(v.begin(), v.end()), v.end());
这行代码做了两件事:std::unique 把 v 变成 {1,2,3,4,3,3,4}(前 4 个是去重后有效段),并返回指向第 5 个元素(第一个冗余位置)的迭代器;erase 则从那个位置删到 v.end(),最终 v 真正变成 {1,2,3,4}。
- 必须传入同一容器的
begin()和end()给unique,不能混用不同容器或临时对象 -
unique要求元素支持operator==,或传入自定义二元谓词(如[](int a, int b) { return a == b; }) - 对
std::list,直接用list.unique()成员函数更高效,无需erase
常见错误:忘了 erase 或用错迭代器范围
下面这些写法都不对:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
// ❌ 错误:只调用 unique,没 erase → 容器 size 不变,末尾垃圾残留
std::unique(v.begin(), v.end());
// ❌ 错误:erase 用错了范围,删多了或删少了
v.erase(v.begin(), std::unique(v.begin(), v.end())); // 删开头,不是删末尾
// ❌ 错误:对未排序 vector 用 unique → 只能去“连续重复”,{1,2,1} 不会变 {1,2}
std::vector<int> w = {1, 2, 1};
std::unique(w.begin(), w.end()); // 结果仍是 {1,2,1},因为 1 和 1 不连续
-
std::unique不等价于“去重集合”,它只处理相邻重复项;真要全量去重,得先sort再unique,或改用std::set/std::unordered_set - 对
std::string同样适用,但注意string::erase返回的是string&,别链式调用出错
性能和兼容性注意点
std::unique 是 O(n) 时间、O(1) 额外空间,稳定(不改变非重复元素的相对顺序)。但它要求迭代器是 ForwardIterator,所以不能用于 std::forward_list 的普通算法版本(得用其成员函数 unique)。
- 对
std::vector、std::deque、std::array直接可用 - 如果容器元素移动代价大(比如含大数组的 struct),考虑用
std::unique_copy到新容器,避免原地搬移 - C++20 起可配合
std::ranges::unique,语法更简洁,但底层逻辑不变
关键就一点:unique 只是“挤出水分”,erase 才是“拧干毛巾”。漏掉 erase,数据看似变了,实际没清理干净——尤其在调试时看到诡异的旧值残留,八成是这里忘了动手。

















