std::views::chunk_by 按相邻元素谓词翻转切分,非全局值分组;仅支持 forward_range,谓词须为无状态二元函数,参数类型严格匹配,且块为 subrange 非容器。

std::views::chunk_by 不是“按值分组”,它只在相邻元素间谓词结果翻转时切一刀;传 std::equal_to{} 或写 (a, b) { return a == b; } 不能把所有相同值归到一块,只能切出连续等值段。
为什么 std::views::chunk_by(std::equal_to{}) 没按你预期分组
常见错误现象:对 {1,2,1,1} 调用 std::views::chunk_by(std::equal_to{}),得到 [1]、[2]、[1,1] —— 看似对,但逻辑和你想的完全不同:
- 首元素
1自动成块,不调用谓词 - 比较
2和前一个1→std::equal_to{}(2, 1)返回false→ 切,[1]结束 - 比较
1和前一个2→false→ 再切,[2]结束 - 比较第二个
1和前一个1→true→ 不切,延续当前块 →[1,1]
它不追溯历史,也不聚合全局相等性。若真要“所有相同值归一组”,得先排序再用 chunk_by,或等 std::ranges::group_by(C++26 草案中)。
std::views::chunk_by 的谓词必须二元、无状态、类型匹配
谓词签名强制为 bool(const T&, const T&),且不能有副作用。以下写法全错:
立即学习“C++免费学习笔记(深入)”;
-
[&counter](int a, int b) mutable { return ++counter % 2; }→ 捕获非常量引用,导致chunk_by_view不可复制,后续接| std::views::transform直接编译失败 -
[](int a, double b) { return a == b; }→ 参数类型不一致,模板推导失败 -
[=](auto& a, auto& b) { return a.name == b.name; }→ 若输入是const std::vector<T>,auto&无法绑定const T&,应写const auto&
安全写法示例:
[](const auto& a, const auto& b) { return a[0] == b[0]; } // 字符串首字母
[=](int a, int b) { return a / 10 == b / 10; } // 按十位分组
std::equal_to{} // 标准对象,天然满足约束
输入范围必须是 forward_range,且生命周期必须可控
std::views::chunk_by 内部需多次解引用同一位置(比如回退取前一元素),因此不支持仅满足 input_range 的单次流(如 std::istream_view),也不支持 std::list 或 std::forward_list —— 它们迭代器失效太敏感,编译直接报 static_assert failed: 'The range argument to views::chunk_by must model forward_range'。
- 可用类型:
std::vector、std::array、std::string、原生数组、std::span - 禁止写法:
std::vector{1,2,3} | std::views::chunk_by(pred)→ 临时std::vector析构后,视图内迭代器悬垂 - 正确写法:
std::vector data = {1,2,3}; auto chunks = data | std::views::chunk_by(pred);
一旦原容器发生重分配(如 vector::push_back),所有已构造的 chunk_by_view::iterator 立即失效 —— 这比 std::views::chunk 严格得多。
别把它和 std::views::chunk 混了
名字像,行为毫无关系:
-
std::views::chunk(3)是按固定长度切:每 3 个元素一组,接受整数参数,签名完全不同 -
std::views::chunk_by只接受二元可调用对象,根本不认识数字 3;写chunk_by(3)编译报错:no matching function for call to 'chunk_by(int)' - 想实现“每 3 个一组”,该用
std::views::chunk(3);想按语义连续性分组(如奇偶、首字母、单调段),才轮到chunk_by
最易被忽略的一点:chunk_by 视图的每个块是 std::ranges::subrange,不是容器 —— 你不能直接对块调用 .size() 或 .data(),得用 std::ranges::size(chunk) 或结构化绑定访问其 begin()/end()。


















