如何让基于std::ranges::zip_view的C++排序代码正常运行?
问题解决思路与修正代码
核心错误分析
你的代码有两个关键问题导致无法运行:
- 比较器类型不匹配:
std::ranges::sort要求第二个参数是二元谓词(接受两个元素并返回布尔值),但你传入的std::ranges::lexicographical_compare是一个独立算法,需要两个范围作为输入,不能直接作为排序比较器。 - zip_view排序的副作用:
std::ranges::views::zip生成的视图元素是原容器的引用,排序时会直接交换indexes和values的元素,导致投影依赖的values数组在排序过程中被修改,破坏了排序依据的原始前缀序列。
修正方案(保留流水线投影+无容器复制)
我们可以通过先排序原始索引、再原地重排容器的方式解决问题,既保留投影的流水线写法,又不会复制原容器:
修正后的代码
#include <vector> #include <iostream> #include <ranges> #include <algorithm> #include <numeric> int main() { std::vector values={7,3,9,2,4,7}; std::vector indexes={0,1,2,3,4,5}; // 创建原始索引数组,记录每个元素的初始位置 std::vector<std::size_t> original_idx(values.size()); std::iota(original_idx.begin(), original_idx.end(), 0); // 基于原始values的反向前缀视图排序索引数组 std::ranges::sort(original_idx, [&values](std::size_t a, std::size_t b) { // 保留流水线式的投影写法 auto proj_a = values | std::ranges::views::take(a + 1) | std::ranges::views::reverse; auto proj_b = values | std::ranges::views::take(b + 1) | std::ranges::views::reverse; // 用字典序比较两个投影视图作为排序依据 return std::ranges::lexicographical_compare(proj_a, proj_b); }); // 根据排序后的索引,原地重排indexes和values(无容器复制) std::ranges::permute(indexes, original_idx); std::ranges::permute(values, original_idx); // 输出结果 for (auto i : indexes) { for (int vi = 0; vi < i; ++vi) { std::cout << values[vi] << " "; } std::cout << std::endl; } }
方案说明
- 原始索引数组:用
std::iota生成0到容器长度-1的索引,确保排序依据是原始values数组的前缀,避免排序过程中修改values导致投影失效。 - 流水线投影保留:比较器中依然使用
values | take(n+1) | reverse的流水线写法生成投影视图,完全符合你的需求。 - 原地重排:使用
std::ranges::permute根据排序后的索引原地调整indexes和values的元素顺序,无需复制整个容器,内存开销极小。
内容的提问来源于stack exchange,提问作者Damir Tenishev
相关产品推荐
相关产品推荐

