使用C++23 zip_view排序两个数组遇编译错误,原因何在?
同时排序两个数组:C++23 zip_view 为何无法工作?
有一个经典任务:将两个数组按同索引元素构成的虚拟对进行排序,这类需求早在10年前就有解决方案(比如Boost的zip_iterator结合std::sort)。
用range-v3库可以轻松实现这个功能:
#include <array> #include <range/v3/all.hpp> int main() { auto x = std::array{ 3, 2, 4, 1 }; auto y = std::array{'A', 'B', 'C', 'D'}; ranges::sort( ranges::views::zip( x, y ) ); // 排序后 x = {1,2,3,4}, y={'D','B','A','C'} }
C++23引入了std::ranges::zip_view,原本想用标准库实现相同逻辑:
#include <array> #include <ranges> #include <algorithm> int main() { auto x = std::array{ 3, 2, 4, 1 }; auto y = std::array{'A', 'B', 'C', 'D'}; std::ranges::sort( std::views::zip( x, y ) ); }
但编译时会出现类似这样的错误(以GCC为例):
/opt/compiler-explorer/gcc-trunk-20221127/include/c++/13.0.0/bits/ranges_algo.h:54:31: error: no matching function for call to '__invoke(std::ranges::less&, std::pair<int, char>&, std::pair<int&, char&>)' 54 | return std::__invoke(__comp, | ~~~~~~~~~~~~~^~~~~~~~ 55 | std::__invoke(__proj, std::forward<_TL>(__lhs)), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 56 | std::__invoke(__proj, std::forward<_TR>(__rhs))); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
问题根源
这不是编译器实现不成熟,而是C++23标准对zip_view的设计导致的:
std::views::zip(x,y)返回的zip_view,其元素类型是tuple<int&, char&>(引用类型的元组)。std::ranges::sort在排序过程中,需要临时存储元素(比如交换、分区操作时),此时会生成值类型的元组tuple<int, char>。- 默认比较器
std::ranges::less无法处理引用元组和值元组之间的比较——这两种类型不匹配,没有对应的operator<重载支持。
而range-v3的zip_view在设计时做了特殊处理,确保比较逻辑能兼容引用和值的混合场景,所以可以直接调用sort。
解决方法
你可以自定义一个比较器,显式处理两种类型的比较:
#include <array> #include <ranges> #include <algorithm> #include <tuple> int main() { auto x = std::array{ 3, 2, 4, 1 }; auto y = std::array{'A', 'B', 'C', 'D'}; std::ranges::sort(std::views::zip(x, y), [](const auto& lhs, const auto& rhs) { return std::tie(lhs) < std::tie(rhs); }); }
std::tie会把任意tuple-like对象转换成引用元组,无论传入的是引用元组还是值元组,都会统一成引用类型的比较逻辑,从而解决类型不匹配的问题。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

