如何运用std::partition进行容器分区操作?

2026-05-06 19:011阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计917个文字,预计阅读时间需要4分钟。

如何运用std::partition进行容器分区操作?

该伪原创内容如下:

常见错误是误以为 std::partitionstd::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::arraystd::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
  • 跨线程使用该迭代器前,确保原容器没被其他线程修改
分界点位置依赖于具体实现和输入分布,不同标准库(libstdc++、libc++、MSVC STL)可能给出不同 it,只要满足语义就合法。别硬编码偏移量。
标签:C

本文共计917个文字,预计阅读时间需要4分钟。

如何运用std::partition进行容器分区操作?

该伪原创内容如下:

常见错误是误以为 std::partitionstd::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::arraystd::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
  • 跨线程使用该迭代器前,确保原容器没被其他线程修改
分界点位置依赖于具体实现和输入分布,不同标准库(libstdc++、libc++、MSVC STL)可能给出不同 it,只要满足语义就合法。别硬编码偏移量。
标签:C