std::ranges::views::chunk_by 根据相邻元素是否满足二元谓词划分逻辑组,当 pred(it, (it+1)) 为 false 时切分,返回各连续满足谓词的最大子序列视图;要求谓词接受两个参数,不满足等价关系,仅单次遍历,O(n) 时间复杂度。

std::ranges::views::chunk_by 的核心行为是什么
std::ranges::views::chunk_by 不是按固定长度切分,而是根据相邻元素间“是否满足某个二元谓词”来划分逻辑组:当 pred(*it, *(it+1)) 为 false 时,就在 it 和 it+1 之间切一刀。它返回的是一个个 std::ranges::subrange(即视图),每个视图对应一个连续的、内部满足“谓词为真”的最大子序列。
常见误解是把它当成 chunk(按长度分块),实际它更接近“连通分量识别”——比如把 {1,2,3,5,6,8} 按“差值为1”分组,得到 {1,2,3}、{5,6}、{8}。
必须用二元谓词,一元谓词会编译失败
传给 chunk_by 的 callable 必须接受两个参数(当前元素和下一个元素),不能只写一个参数的 lambda。否则 GCC/Clang 会报类似 no matching function for call to 'chunk_by' 的错误,且提示模糊。
- ✅ 正确:
views::chunk_by([](int a, int b) { return b == a + 1; }) - ❌ 错误:
views::chunk_by([](int x) { return x % 2 == 0; })(这是filter或partition_by的思路) - ⚠️ 注意:谓词作用于相邻对,所以输入范围至少要有 1 个元素;空范围或单元素范围也能工作,但只生成一个子视图
与 std::ranges::views::partition_by 的关键区别
partition_by 是基于“等价关系”分组(即谓词需满足自反、对称、传递),而 chunk_by 只看相邻,不要求传递性。这意味着:
立即学习“C++免费学习笔记(深入)”;
- 对序列
{1,3,2,4},用[](int a, int b) { return a % 2 == b % 2; }(同奇偶):
→chunk_by得到{1,3}、{2}、{4}(因为 3→2 奇→偶,断开;2→4 偶→偶,不断)
→partition_by得到{1,3}、{2,4}(全局聚类) -
chunk_by性能是 O(n),仅单次遍历;partition_by在某些实现中可能隐含多次扫描或额外存储 - 如果你要按“单调递增段”切分,用
chunk_by([](int a, int b) { return a ;但注意 <code>{3,1,2}会被切成{3}、{1,2},不是最长递增子序列
实际使用时容易漏掉的细节
返回的每个 chunk 是一个 subrange,不是 vector 或数组。直接对它取 .size() 需要 std::ranges::distance(因为不是所有 range 都支持 size());想转成容器得显式构造:
auto chunks = vec | views::chunk_by([](int a, int b) { return b == a + 1; });
for (auto chunk : chunks) {
std::vector<int> v(chunk.begin(), chunk.end()); // 必须这样构造
// 或用 ranges::to<vector>(chunk)(C++23)
}
另外,chunk_by 是 lazy view,底层 range 被修改后,已生成的 chunk 视图可能失效;若需稳定数据,务必及时 materialize。
它的约束很窄:要求 range 是 forward_range 且可比较迭代器,但不支持输入迭代器(比如 std::istream_view)——这点比 filter 或 transform 更严格。


















