c++oding="utf-8" ?>
std::ranges::views::chunk_by按相邻元素二元谓词结果分组,pred(a,b)为true则b属a所在组,否则断开;要求forward_range、谓词对称传递,非一元函数,典型应用如等值/同号/奇偶分组。

chunk_by 的核心行为:按相邻元素的谓词结果分组
std::ranges::views::chunk_by 不是按固定长度切分,而是根据一个二元谓词(std::binary_predicate)判断「相邻两个元素是否属于同一组」。只要 pred(a, b) 返回 true,就认为 b 属于 a 所在的组;一旦返回 false,就在此处断开,开始新组。
这和 std::ranges::views::chunk(按固定大小切片)完全不同,也和 Python 的 itertools.groupby 行为高度一致——但注意:C++ 版本要求输入范围是 forward_range,且谓词必须满足对称性与传递性(实际中常用于等价关系,比如相等、同符号、同首字母等)。
- 输入必须是
forward_range,不能是仅支持单次遍历的input_range - 谓词接收的是「前一个元素」和「当前元素」,顺序不能反;例如
[1,2,2,3,3,3]用std::equal_to{}分组,会得到{1}, {2,2}, {3,3,3} - 空 range 或单元素 range 会返回一个空视图或单个子视图,行为稳定
常见错误:谓词写成一元函数或逻辑颠倒
最常踩的坑是把 chunk_by 当成「对每个元素单独判断」来用,写成类似 [](int x) { return x > 0; } 的一元 lambda ——编译直接失败,因为 chunk_by 明确要求二元谓词。
另一个典型错误是谓词逻辑反了:比如想按正负分组,却写 [](int a, int b) { return a > 0 && b > 0; }。这会导致 [1,2,-3,-4] 被切成 {1,2}, {-3}, {-4}(因为 2 和 -3 间谓词为 false,断开;但 -3 和 -4 间谓词仍是 false,不连通)。正确做法是判断「是否同号」:[](int a, int b) { return (a > 0) == (b > 0); }
- 错误示例:
views::chunk_by([](auto x) { return x % 2 == 0; })→ 编译报错:no matching function for call to 'chunk_by' - 正确写法:
views::chunk_by([](auto a, auto b) { return (a & 1) == (b & 1); })(按奇偶分组) - 谓词应尽量无副作用,且不要依赖外部状态(如捕获可变变量),否则行为不可预测
配合 views::transform 和容器转换的实际用法
chunk_by 返回的是一个「视图的视图」:外层是组的序列,每组本身又是一个视图。如果要提取每组的首元素、长度或聚合结果,需再套一层操作。
例如从字符串中提取连续重复字符段:"aaabbbccda" → {"aaa","bbb","cc","d","a"}:
std::string s = "aaabbbccda";
auto groups = s | std::views::chunk_by(std::equal_to{})
| std::views::transform([](auto&& rng) {
return std::string(rng.begin(), rng.end());
});
注意:rng 是 std::ranges::subrange,不能直接用 std::string(rng)(构造函数不接受 subrange),必须显式用迭代器构造。
- 想取每组长度?用
std::ranges::distance(rng) - 想取每组最大值?用
std::ranges::max_element(rng),但注意确保非空 - 要存进
std::vector?得加| std::ranges::to<:vector></:vector>(C++23),否则只是懒求值视图
性能与兼容性注意事项
chunk_by 是懒求值的,不缓存中间结果,每次迭代都重新计算谓词。这意味着:如果谓词开销大(比如涉及字符串比较或函数调用),且你多次遍历同一组,性能会明显下降。
它在 GCC 13+ 和 Clang 16+ 中才完全可用(libstdc++ 和 libc++ 均已实现),MSVC 2022 17.5+ 支持,但早期版本可能只提供实验性支持(需定义 _GLIBCXX_USE_CXX23 或启用 C++23 标准)。
- 调试时看不到完整分组结构:GDB/LLDB 对视图支持有限,建议先转成
std::vector再观察 - 不能用
auto捕获chunk_by结果后跨作用域使用,因为内部迭代器可能失效(尤其源 range 是临时对象时) - 若源 range 是
std::vector的const&,没问题;但如果是局部std::vector的view,必须确保其生命周期覆盖所有视图使用点
真正麻烦的地方不在语法,而在谓词设计——它必须准确刻画「什么才算‘连续同类’」,稍有偏差,分组就碎成一粒一粒。写完务必用边界 case 测:空输入、单元素、全相同、全不同、交替模式。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











