std::unique_copy仅去除相邻重复元素且不修改原容器;需源区间已排序或接受仅去重连续块语义,目标区间须预分配空间并用返回迭代器截断。

std::unique_copy 只能去除相邻重复元素,不能全局去重;它不修改原容器,而是把去重结果复制到目标区间
std::unique_copy 的作用范围和前提条件
这个函数只识别并跳过「紧挨着的重复项」,比如 {1,2,2,3,3,3,4} 会变成 {1,2,3,4},但 {1,2,3,2,4} 中的第二个 2 不会被删——因为它前面是 3,不连续。
- 输入迭代器必须支持前向遍历(至少是 ForwardIterator)
- 源区间必须已按某种规则排好序,或你明确接受“仅去重连续块”的语义
- 目标容器/区间要有足够空间容纳输出(返回值是目标末尾迭代器,务必用它来截断)
正确调用 std::unique_copy 的三要素
常见错误是忽略返回值、没预留空间、或误以为它会自动 shrink 目标容器。实际需手动处理长度:
std::vector src = {1,1,2,2,2,3,4,4}; std::vector dst(src.size()); // 预分配,避免 reallocate 影响迭代器有效性 auto last = std::unique_copy(src.begin(), src.end(), dst.begin()); dst.erase(last, dst.end()); // 关键:用返回的迭代器清理多余元素
- 第三个参数是目标起始位置,不是容器本身
- 返回值是「写入结束位置」,类型与第三个参数相同(这里是
dst.begin()类型) - 如果目标是 raw Array 或
std::array,不能调erase,得自己记下长度
配合自定义比较逻辑的注意事项
默认用 operator==,但可通过第四个参数传入二元谓词。注意:该谓词仍只比较相邻元素,且必须满足等价关系(自反、对称、传递),否则行为未定义:
立即学习“C++免费学习笔记(深入)”;
// 忽略大小写的字符串去重(连续) std::vector words = {"Hello", "HELLO", "world", "WORLD", "hello"}; std::vector uniq; uniq.resize(words.size()); auto last = std::unique_copy(words.begin(), words.end(), uniq.begin(), [](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); }); }); uniq.erase(last, uniq.end());
- 谓词接收的是「当前元素」和「前一个元素」,顺序固定为
(prev, curr) - 不能在里面修改元素,也不能抛异常(c++17 起要求谓词是 noexcept)
- 如果源数据没按该谓词“等价”分组,结果不可预测(例如先小写后大写混排)
和 std::unique 的关键区别在哪
最常混淆的点:两者语义不同,适用场景也不同。
-
std::unique在原容器上操作,返回「新逻辑末尾」,需配合erase才真正缩容;std::unique_copy完全不碰源数据 -
std::unique要求双向迭代器(因要 erase),std::unique_copy只需前向迭代器 - 如果目标是输出到 ostream_iterator 或 transform 后再复制,只能用
unique_copy - 性能上,
unique_copy多一次内存拷贝,但避免了原容器的移动/赋值开销
连续去重这件事本身很简单,难的是意识到它不解决「重复元素散落各处」的问题——真要全局唯一,得先排序或换用 std::set / std::unordered_set 做标记。