std::unique_copy只移除相邻重复元素,不进行全局去重;需先排序或保证有序,且目标区间不可与源区间重叠,返回目标新末尾需手动截断。

std::unique_copy 为什么没去重?
它只移除「相邻重复元素」,不是全容器去重。如果你的输入没排序或没排好序,std::unique_copy 看到 [1,3,1] 会原样拷贝——因为 1 和 3 不同、3 和 1 也不同,根本不会触发“去重逻辑”。
常见错误现象:std::unique_copy 返回的迭代器长度和原容器一样,输出里还有重复值;或者只删掉了开头连续重复(比如 [2,2,2,1,1] 变成 [2,1,1]),后面漏了。
- 必须先用
std::sort(或保证输入已严格有序) - 如果要保留原始顺序且去重,得换方案:比如用
std::unordered_set辅助判断,自己写循环 -
std::unique_copy不改变原容器,只负责把“去重后”的序列写进目标区间——目标空间得提前分配好,或用插入迭代器(如std::back_inserter)
怎么安全调用 std::unique_copy?
核心是配对使用「源范围 + 目标起始」,且注意返回值——它返回的是目标区间的“新末尾”,不是原容器的长度。
典型误用:std::unique_copy(v.begin(), v.end(), v.begin()),想原地操作,但结果可能越界或覆盖未读数据(因为读写区域重叠且没按正确顺序处理)。
立即学习“C++免费学习笔记(深入)”;
- 永远不要让目标区间与源区间重叠,除非用
std::unique(它专为原地设计) - 目标容器要有足够空间,或用
std::vector::reserve()预留,否则std::back_inserter更稳妥 - 记住它的返回值类型是目标迭代器,要接住它才能知道实际写了多少:
auto it = std::unique_copy(...)
std::vector<int> src = {1,2,2,3,3,4};
std::vector<int> dst;
dst.reserve(src.size());
auto end = std::unique_copy(src.begin(), src.end(), dst.begin());
dst.erase(end, dst.end()); // 必须手动截断多余空间
std::unique_copy 和 std::unique 的关键区别
名字像,行为差很远:std::unique 是原地收缩容器(移动元素、返回新逻辑末尾),std::unique_copy 是纯函数式拷贝,不碰源数据。
性能影响:前者少一次内存分配,但要求可写入源容器;后者更安全、更灵活(比如从 std::list 拷到 std::vector),但多一次遍历+拷贝开销。
-
std::unique要求迭代器可写(不能用于const容器或只读视图) -
std::unique_copy支持任意输入范围 + 任意输出目标,包括 C 数组、流迭代器等 - 两者都只处理相邻重复,都不做
O(n²)全局查重
自定义比较时容易忽略的约束
传给 std::unique_copy 的二元谓词(比如 [](int a, int b) { return a == b; })必须满足「等价关系」:自反、对称、传递。否则行为未定义——尤其在浮点数或自定义类型中容易翻车。
常见坑:abs(a - b) 看似合理,但它不满足传递性(a≈b 且 b≈c,不代表 a≈c),会导致去重结果不稳定。
- 优先用
std::equal_to<>或默认比较,除非你明确需要并验证过自定义逻辑 - 如果真要模糊比较,改用预处理(如四舍五入后存整数)比在谓词里硬搞更可靠
- 谓词接收的是「当前元素和前一个元素」,不是任意两个元素,别写成全局查重逻辑
复杂点在于:它不报错,只是结果不可预测。调试时很难定位到是谓词写的不对,而容易怀疑数据或调用方式。











