std::views::chunk_by 是 c++23 范围库中用于按相邻元素逻辑关系动态分块的视图适配器,通过二元谓词判断相邻元素是否属于同一组,支持惰性求值、零拷贝及 forwardrange,适用于奇偶分组、首字母分段、等值聚合等场景。

如果您在使用 C++23 范围库对序列进行逻辑分组时,发现元素需按相邻关系动态切分而非固定长度,则 std::views::chunk_by 是专为此类场景设计的视图适配器。以下是对其原理与用法的详细展开:
一、chunk_by 的核心行为机制
std::views::chunk_by 接收一个输入范围和一个二元谓词(binary predicate),它不依据索引或固定大小,而是逐对比较相邻元素:当谓词对当前元素与前一元素的返回值发生**变化**(从 true 变 false 或反之)时,即触发新块的起始。该行为等价于 Python 中 itertools.groupby 的默认模式,但不依赖相等性,而由用户定义的任意逻辑驱动。
1、谓词被调用形式为 pred(*it, *(it-1)),即作用于 (当前, 前一) 元素对;
2、首个元素自动成为第一块的起点,无需前置比较;
3、输出为 std::ranges::chunk_by_view,其每个迭代器解引用得到一个 std::ranges::subrange,指向原范围中连续满足同一谓词结果的子段;
4、该视图是惰性求值、零拷贝的,不修改原始数据,也不要求 RandomAccessRange —— ForwardRange 即可支持。
二、基于奇偶性切分整数序列
此方法利用谓词判断相邻元素是否同为奇数或偶数,从而将连续同奇偶性的数字归为一组,适用于信号分段、状态持续区间识别等场景。
1、声明一个 std::vector
2、构造 chunk_by 视图:auto chunks = v | std::views::chunk_by([](int a, int b) { return (a % 2) == (b % 2); });
3、遍历输出各块:for (const auto& chunk : chunks) { /* 处理 subrange */ };
4、结果为三块:[2,4,6]、[1,3,5]、[8,10,7] —— 注意末尾 7 与前一元素 10 奇偶不同,故自成一块。
三、按字符串首字母分组
当处理字符串容器且需按字典序连续段切分时,chunk_by 可避免预排序,直接捕获自然顺序中的“首字母跃变”点,保留原始位置信息。
1、准备 std::vector<:string> words = {"apple", "avocado", "banana", "cherry", "cranberry"};
2、定义谓词:[](const std::string& a, const std::string& b) { return a[0] == b[0]; };
3、应用视图:auto grouped = words | std::views::chunk_by(pred);
4、迭代时每块包含首字母相同的连续字符串:{"apple","avocado"}、{"banana"}、{"cherry","cranberry"}。
四、使用 std::equal_to 模拟 groupby 等值分组
尽管 std::views::chunk_by 不接受一元谓词,但传入 std::equal_to{} 可实现最常用的相邻相等分组,语义上等效于传统 groupby,且无需额外头文件。
1、初始化 std::vector
2、写法为:auto groups = data | std::views::chunk_by(std::equal_to{});
3、注意:std::equal_to{} 是函数对象,其 operator() 接收两个参数,完全匹配 chunk_by 所需签名;
4、输出四块:[1,1,1]、[2,2]、[3,3,3,3] —— 每块内所有元素值相同,且与前后块不同。
五、处理空范围与单元素范围
chunk_by 对边界情况有明确定义:空范围产生空视图;单元素范围产生仅含一个子范围的视图,该子范围即为原元素构成的 subrange,不执行任何谓词调用。
1、对空 vector
2、对单元素 vector
3、谓词在此类情况下**永不调用**,因此无需担心空指针、越界或未定义行为;
4、该特性保证了 chunk_by 在泛型算法中可安全嵌入,无需前置 size() 判断。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











