如何用C++20/C++23 ranges库实现可交换的双层Zip视图?
解决方案:创建可支持排序的双层Zip视图
你的核心问题在于:之前通过transform返回的内层zip视图是临时视图类型,这类视图不满足swappable概念,因此无法被sort等算法操作。要解决这个问题,需要让视图的元素成为能同步修改底层数据的可交换实体,或者直接对底层可交换的元素视图进行排序。
方案一:直接对zip(v1, v2)视图排序(无需自定义类型)
std::ranges::views::zip(v1, v2)生成的视图元素是std::pair<std::array<int,2>&, std::array<int,2>&>,本身支持交换(交换时会同步修改v1和v2中对应的数组)。我们只需自定义比较逻辑,让排序依据双层Zip后的结构即可。
示例代码
#include <vector> #include <array> #include <ranges> #include <algorithm> #include <iostream> // 打印双层Zip结构 void print_double_zip(const auto& v1, const auto& v2) { auto double_zip = std::ranges::views::zip(v1, v2) | std::ranges::views::transform([](const auto& p) { return std::ranges::views::zip(std::get<0>(p), std::get<1>(p)); }); std::cout << "["; bool first = true; for (const auto& inner : double_zip) { if (!first) std::cout << ", "; first = false; std::cout << "["; bool inner_first = true; for (const auto& p : inner) { if (!inner_first) std::cout << ", "; inner_first = false; std::cout << "(" << p.first << ", " << p.second << ")"; } std::cout << "]"; } std::cout << "]\n"; } int main() { std::vector<std::array<int, 2>> v1 = { {5, 6}, {1, 2}, {3, 4} }; std::vector<std::array<int, 2>> v2 = { {11, 12}, {7, 8}, {9, 10} }; std::cout << "排序前:\n"; print_double_zip(v1, v2); // 对Zip视图排序,比较双层Zip的字典序 auto zip_v = std::ranges::views::zip(v1, v2); std::ranges::sort(zip_v, [](const auto& lhs, const auto& rhs) { auto lhs_inner = std::ranges::views::zip(std::get<0>(lhs), std::get<1>(lhs)); auto rhs_inner = std::ranges::views::zip(std::get<0>(rhs), std::get<1>(rhs)); return std::ranges::lexicographical_compare(lhs_inner, rhs_inner); }); std::cout << "排序后:\n"; print_double_zip(v1, v2); return 0; }
方案二:自定义代理对象,创建可直接排序的双层Zip视图
如果希望直接对双层Zip视图执行排序,可以自定义一个代理结构体,它持有对v1和v2数组的引用,支持交换和比较操作,同时能返回内层的Zip视图用于访问元素。
示例代码
#include <vector> #include <array> #include <ranges> #include <algorithm> #include <iostream> // 代理结构体:持有两个数组的引用,支持交换和比较 struct ZipPairProxy { std::array<int, 2>& arr1; std::array<int, 2>& arr2; // 返回内层Zip视图,用于访问/修改元素 auto inner_zip() const { return std::ranges::views::zip(arr1, arr2); } // 按双层Zip的字典序比较 friend bool operator<(const ZipPairProxy& lhs, const ZipPairProxy& rhs) { return std::ranges::lexicographical_compare(lhs.inner_zip(), rhs.inner_zip()); } // 交换代理,同步修改底层数组 friend void swap(ZipPairProxy& lhs, ZipPairProxy& rhs) noexcept { using std::swap; swap(lhs.arr1, rhs.arr1); swap(lhs.arr2, rhs.arr2); } }; // 打印双层Zip视图 void print_double_zip_view(const auto& view) { std::cout << "["; bool first = true; for (const auto& proxy : view) { if (!first) std::cout << ", "; first = false; std::cout << "["; bool inner_first = true; for (const auto& p : proxy.inner_zip()) { if (!inner_first) std::cout << ", "; inner_first = false; std::cout << "(" << p.first << ", " << p.second << ")"; } std::cout << "]"; } std::cout << "]\n"; } int main() { std::vector<std::array<int, 2>> v1 = { {5, 6}, {1, 2}, {3, 4} }; std::vector<std::array<int, 2>> v2 = { {11, 12}, {7, 8}, {9, 10} }; // 创建可排序的双层Zip视图 auto double_zip_view = std::ranges::views::zip(v1, v2) | std::ranges::views::transform([](auto& p) -> ZipPairProxy { return {std::get<0>(p), std::get<1>(p)}; }); std::cout << "排序前:\n"; print_double_zip_view(double_zip_view); // 直接对视图执行排序 std::ranges::sort(double_zip_view); std::cout << "排序后:\n"; print_double_zip_view(double_zip_view); // 修改视图中的元素(会同步修改v1和v2) auto first_proxy = *double_zip_view.begin(); auto first_inner = *first_proxy.inner_zip().begin(); first_inner.first = 100; first_inner.second = 200; std::cout << "修改后:\n"; print_double_zip_view(double_zip_view); return 0; }
原实现失效的原因
你之前的代码通过transform返回views::zip的结果,生成的是transform_view<zip_view<...>, ...>,其元素类型是视图对象而非可交换的实体。交换这类视图对象不会修改底层的v1和v2,且标准库视图通常不满足swappable概念,因此无法被sort等算法操作。
内容的提问来源于stack exchange,提问作者lc_vorenus
相关产品推荐
相关产品推荐

