能否使range::remove作用于views::chunk?求解决方案
无法用ranges::remove操作ranges::views::chunk视图的问题及解决办法
经测试发现ranges::remove无法对ranges::views::chunk生成的视图进行操作,尝试移除首个元素为9的chunk时触发了concept约束错误。
测试代码如下:
#include "range/v3/algorithm/remove.hpp" #include <iostream> #include <range/v3/view/chunk.hpp> #include <vector> int main() { std::vector<int> v{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; auto a = ranges::views::chunk(v, 4); for (const auto& e: a) std::cout << e[0] << "\n"; auto it = ranges::remove(a, 9, [](const auto& e) { return e[0]; }); // expected result (xx means unspecified) // std::vector<int> v{ // 1, 2, 3, 4, // 5, 6, 7, 8, // 13, 14, 15, 16, // xx, xx, xx, xx // }; }
报错信息显示该操作不满足permutable、forward_iterator等一系列concepts约束,具体错误片段如下:
constraints not satisfied [with I = ranges::chunk_view<ranges::ref_view<std::vector<int>>>, S = int, T = (lambda at /home/oliver/c/physicssim/app/parremove.cpp:19:36), P = ranges::identity] constexpr I RANGES_FUNC(remove)(I first, S last, T const & val, P proj = P{}) ^ /home/oliver/c/physicssim/ext/range-v3/include/range/v3/detail/config.hpp:633:27: note: expanded from macro 'RANGES_FUNC' #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/ ^ /home/oliver/c/physicssim/ext/range-v3/include/range/v3/algorithm/remove.hpp:42:22: note: because 'ranges::chunk_view<ranges::ref_view<std::vector<int>>>' does not satisfy 'permutable' requires permutable<I> AND sentinel_for<S, I> AND ^ /home/oliver/c/physicssim/ext/range-v3/include/range/v3/detail/prologue.hpp:35:26: note: expanded from macro 'template' template<__VA_ARGS__ CPP_TEMPLATE_AUX_ \ ^ /home/oliver/c/physicssim/ext/range-v3/include/range/v3/iterator/concepts.hpp:841:9: note: because 'ranges::chunk_view<ranges::ref_view<std::vector<int>>>' does not satisfy 'forward_iterator' forward_iterator<I> && ^ /home/oliver/c/physicssim/ext/range-v3/include/range/v3/iterator/concepts.hpp:371:9: note: because 'ranges::chunk_view<ranges::ref_view<std::vector<int>>>' does not satisfy 'input_iterator' input_iterator<I> && ^ /home/oliver/c/physicssim/ext/range-v3/include/range/v3/iterator/concepts.hpp:363:9: note: because 'ranges::chunk_view<ranges::ref_view<std::vector<int>>>' does not satisfy 'input_or_output_iterator' input_or_output_iterator<I> && ^ ....
问题原因
ranges::remove是原地修改算法,要求迭代器满足permutable(可置换)约束——意味着迭代器指向的元素可以被移动、交换,且迭代器本身支持双向遍历。但chunk_view的迭代器只是原容器的分段视图引用,它的"元素"是临时的子范围对象,并非可直接修改的独立元素,根本无法满足permutable的核心要求,所以会触发约束错误。
替代方案
方案1:直接操作原容器(高效,原地修改)
每个chunk对应原容器中连续的固定长度元素,要移除目标chunk,等价于移除原容器中对应的连续区间。比如要移除首个元素为9的chunk,直接定位到该元素在原容器中的位置,再按chunk大小计算要删除的区间:
#include "range/v3/algorithm/find.hpp" #include <iostream> #include <vector> int main() { std::vector<int> v{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; // 找到首个值为9的元素的迭代器 auto target = ranges::find(v, 9); if (target != v.end()) { // 计算该元素所在chunk的起始位置(对齐到chunk大小的倍数) auto chunk_idx = (target - v.begin()) / 4; auto chunk_start = v.begin() + chunk_idx * 4; // 删除整个chunk v.erase(chunk_start, chunk_start + 4); } // 输出验证 for (int num : v) { std::cout << num << " "; } // 输出:1 2 3 4 5 6 7 8 13 14 15 16 }
方案2:转换为容器后操作(灵活,但会复制数据)
如果不想直接操作原容器,可以先把chunk视图转换成实际的容器(比如vector<vector<int>>),移除目标chunk后再合并回原容器:
#include "range/v3/algorithm/remove_if.hpp" #include <iostream> #include <range/v3/view/chunk.hpp> #include <vector> int main() { std::vector<int> v{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; auto a = ranges::views::chunk(v, 4); // 将视图转为实际容器 std::vector<std::vector<int>> chunks(a.begin(), a.end()); // 移除首个元素为9的chunk chunks.erase(ranges::remove_if(chunks.begin(), chunks.end(), [](const auto& chunk) { return chunk[0] == 9; }), chunks.end()); // 合并回原容器 v.clear(); for (const auto& chunk : chunks) { v.insert(v.end(), chunk.begin(), chunk.end()); } // 输出验证 for (int num : v) { std::cout << num << " "; } }
方案3:使用filter视图过滤(不修改原容器,仅用于遍历)
如果不需要修改原容器,只是想在遍历时跳过目标chunk,直接用views::filter即可:
#include <iostream> #include <range/v3/view/chunk.hpp> #include <range/v3/view/filter.hpp> #include <vector> int main() { std::vector<int> v{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; auto filtered_chunks = ranges::views::chunk(v, 4) | ranges::views::filter([](const auto& chunk) { return chunk[0] != 9; }); // 遍历过滤后的视图 for (const auto& chunk : filtered_chunks) { std::cout << chunk[0] << "\n"; } // 输出:1 5 13 }
内容的提问来源于stack exchange,提问作者Oliver Schönrock
相关产品推荐
相关产品推荐

