如何运用std::partition进行容器分区操作?
- 内容介绍
- 文章标签
- 相关推荐
本文共计917个文字,预计阅读时间需要4分钟。
该伪原创内容如下:
常见错误是误以为 std::partition 像 std::sort 那样“整理”数据,结果发现偶数/奇数分组后,偶数之间顺序乱了,还以为是迭代器写错了。
- 适用场景:快速二分类(如把空指针移到末尾、把无效ID过滤到后面)
- 不适用场景:需要按原始顺序输出满足条件的子序列
- 性能上,
std::partition是 O(n),单次遍历;std::stable_partition通常是 O(n log n) 或额外 O(n) 空间
传入的谓词必须是纯函数,不能有副作用
std::partition 内部可能对同一元素多次调用谓词(尤其在某些标准库实现中做优化判断时),如果谓词里修改了外部状态(比如递增计数器、打印日志、改变全局变量),行为不可预测。
示例中容易踩的坑:
立即学习“C++免费学习笔记(深入)”;
int count = 0; auto pred = [&count](int x) { return ++count, x > 0; }; // ❌ 危险!count 增加次数不确定 std::vector<int> v = {1, -2, 3}; std::partition(v.begin(), v.end(), pred); // count 最终可能是 2、3 或 4
- 谓词应只读取参数和捕获的 const 变量
- 若需调试,用
std::copy_if+ 手动构造新容器更安全 - lambda 捕获列表推荐用
[=]或[&] + const显式约束
迭代器范围必须合法,且不能是 const_iterator
std::partition 要求输入的是可写迭代器(RandomAccessIterator,且 value_type 可移动/交换),所以不能传 v.cbegin()、std::as_const(v).begin(),也不能对 const std::vector<T> 调用。
错误现象通常是编译失败,报错信息类似:
error: no matching function for call to 'partition(... const_iterator ...)'
- 确保用
v.begin()/v.end(),而非v.cbegin() - 容器本身不能是 const 限定的
- 对
std::array、std::deque同样适用,但std::list推荐用其成员函数list::partition(更高效)
返回值是指向第二分区起始位置的迭代器
这个返回值非常关键,但常被忽略。它不是布尔值,也不是 void —— 它指向第一个「不满足谓词」的元素,也就是前后两段的分界点。
你可以直接用它切分数据,比如提取所有满足条件的元素:
std::vector<int> v = {1, -2, 3, -4, 5}; auto it = std::partition(v.begin(), v.end(), [](int x) { return x > 0; }); // it 指向 -2 或 -4(取决于实现),但一定在所有正数之后、负数之前 std::vector<int> positives(v.begin(), it); // ✅ 安全截取
- 别假设
it == v.end()表示“全满足”,要实际检查 - 如果后续还要对前半段操作,直接用
v.begin()到it这段,别重新partition - 跨线程使用该迭代器前,确保原容器没被其他线程修改
it,只要满足语义就合法。别硬编码偏移量。本文共计917个文字,预计阅读时间需要4分钟。
该伪原创内容如下:
常见错误是误以为 std::partition 像 std::sort 那样“整理”数据,结果发现偶数/奇数分组后,偶数之间顺序乱了,还以为是迭代器写错了。
- 适用场景:快速二分类(如把空指针移到末尾、把无效ID过滤到后面)
- 不适用场景:需要按原始顺序输出满足条件的子序列
- 性能上,
std::partition是 O(n),单次遍历;std::stable_partition通常是 O(n log n) 或额外 O(n) 空间
传入的谓词必须是纯函数,不能有副作用
std::partition 内部可能对同一元素多次调用谓词(尤其在某些标准库实现中做优化判断时),如果谓词里修改了外部状态(比如递增计数器、打印日志、改变全局变量),行为不可预测。
示例中容易踩的坑:
立即学习“C++免费学习笔记(深入)”;
int count = 0; auto pred = [&count](int x) { return ++count, x > 0; }; // ❌ 危险!count 增加次数不确定 std::vector<int> v = {1, -2, 3}; std::partition(v.begin(), v.end(), pred); // count 最终可能是 2、3 或 4
- 谓词应只读取参数和捕获的 const 变量
- 若需调试,用
std::copy_if+ 手动构造新容器更安全 - lambda 捕获列表推荐用
[=]或[&] + const显式约束
迭代器范围必须合法,且不能是 const_iterator
std::partition 要求输入的是可写迭代器(RandomAccessIterator,且 value_type 可移动/交换),所以不能传 v.cbegin()、std::as_const(v).begin(),也不能对 const std::vector<T> 调用。
错误现象通常是编译失败,报错信息类似:
error: no matching function for call to 'partition(... const_iterator ...)'
- 确保用
v.begin()/v.end(),而非v.cbegin() - 容器本身不能是 const 限定的
- 对
std::array、std::deque同样适用,但std::list推荐用其成员函数list::partition(更高效)
返回值是指向第二分区起始位置的迭代器
这个返回值非常关键,但常被忽略。它不是布尔值,也不是 void —— 它指向第一个「不满足谓词」的元素,也就是前后两段的分界点。
你可以直接用它切分数据,比如提取所有满足条件的元素:
std::vector<int> v = {1, -2, 3, -4, 5}; auto it = std::partition(v.begin(), v.end(), [](int x) { return x > 0; }); // it 指向 -2 或 -4(取决于实现),但一定在所有正数之后、负数之前 std::vector<int> positives(v.begin(), it); // ✅ 安全截取
- 别假设
it == v.end()表示“全满足”,要实际检查 - 如果后续还要对前半段操作,直接用
v.begin()到it这段,别重新partition - 跨线程使用该迭代器前,确保原容器没被其他线程修改
it,只要满足语义就合法。别硬编码偏移量。
