std::unique配合erase是删除相邻重复元素的标准安全做法,但需先排序确保重复项相邻,仅调用unique不改变容器大小且不析构元素,必须用erase清除尾部残留。

直接说结论:用 std::unique 配合 erase 是标准、安全、高效的做法,但必须注意它只删“相邻重复”,且不会真正缩短容器——漏掉 erase 这一步,数据还在内存里,只是被覆盖了。
为什么 std::unique 不能单独删除元素
std::unique 的行为是“把重复的相邻元素往前挪,返回一个新末尾迭代器”,它不改变容器大小,也不调用析构函数。常见错误是只写 std::unique(v.begin(), v.end()) 就以为删完了。
典型误用现象:v.size() 没变,打印出来好像还有重复值(其实是旧值残留)。
- 它只比较
*(i)和*(i-1),不扫描全量去重 - 输入必须已排序或已按重复组相邻排列,否则无效
- 返回的是“逻辑新终点”,不是容器新 size
正确删除相邻重复的三步写法
标准组合拳:std::unique + erase + end()。这是唯一推荐的、符合 RAII 和迭代器失效规则的写法。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
std::vector<int> v = {1, 1, 2, 2, 2, 3, 4, 4, 5};
auto new_end = std::unique(v.begin(), v.end());
v.erase(new_end, v.end()); // 必须这一步
// v 现在是 {1, 2, 3, 4, 5}
- 顺序不能反:先
unique得到新终点,再用erase清理尾部 - 不能用
v.resize(std::distance(v.begin(), new_end))替代erase,对自定义类型可能跳过析构 - 若 vector 存储的是指针或智能指针,
unique比较的是指针值,不是所指对象内容
自定义比较逻辑怎么写(比如忽略大小写去重字符串)
传入 lambda 或函数对象给 std::unique 第三个参数,让它按你定义的“相等”来判断相邻项。
std::vector<std::string> words = {"Apple", "apple", "Banana", "banana"};
auto new_end = std::unique(words.begin(), words.end(),
[](const std::string& a, const std::string& b) {
return std::equal(a.begin(), a.end(), b.begin(), b.end(),
[](char x, char y) { return std::tolower(x) == std::tolower(y); });
});
words.erase(new_end, words.end());
- lambda 必须接受两个同类型参数,返回
bool - 注意:这个例子前提是大小写混排的字符串已经“相邻”了;如果没排好序,得先
std::sort再unique - 性能敏感时避免在 lambda 里做深拷贝或分配,比如传
const std::string&而非值
最容易被忽略的一点:如果你的 vector 是从别的算法结果拼接来的,或者中间插入过元素,相邻性很可能已被破坏——这时候 std::unique 不是你该用的工具,得换 std::set 或哈希去重,但那就不是“删相邻”而是“删全局重复”了。

















