如何无需lambda对std::tuple形式的zip向量使用投影排序?
标准库ranges中zip视图排序的投影替代方案
问题描述
我可以通过lambda指定按第一个向量排序,用std::ranges::sort对zip后的向量完成排序,但想知道能否像示例(2)(3)那样,通过指定tuple字段名(或其他方式)用投影实现该功能?
之前在ranges3中示例(2)可行,因为当时元素以std::pair实现,能通过&std::pair<int, int>::first访问;但标准库ranges里zip返回的是tuple,这种方式不再生效。
我希望不使用lambda实现需求,原因是需要将比较器和投影分离,方便用于自定义优化版本;同时减少代码量,让代码更易编写、阅读和维护。
测试代码如下:
#include <algorithm> #include <ranges> #include <vector> int main() { std::vector<float> x_values = { 3.0f, 1.0f, 2.0f }; std::vector<float> y_values = { 5.0f, 6.0f, 7.0f }; // (1) 可编译 std::ranges::sort(std::ranges::views::zip(x_values, y_values), [](const auto& lhs, const auto& rhs) { return std::get<0>(lhs) < std::get<0>(rhs); }); // (2) 无法编译,因标准库ranges使用tuple std::ranges::sort(std::ranges::views::zip(x_values, y_values), std::less<>(), &std::pair<int, int>::first); // (3) 推测无法编译,因get是成员函数模板而非成员字段 std::ranges::sort(std::ranges::views::zip(x_values, y_values), std::less<>(), &std::get<0>); }
解决方案
可以通过以下几种方式实现无lambda的投影排序:
1. 使用std::ranges::get<0>作为投影
标准库提供的std::ranges::get<N>是适配range投影的函数对象,直接传入即可:
std::ranges::sort(std::ranges::views::zip(x_values, y_values), std::less<>(), std::ranges::get<0>);
它符合std::ranges::sort对投影的要求——接收zip视图的元素(tuple),返回第一个元素的值,完美替代lambda的逻辑。
2. 自定义可读性更强的投影函数对象
如果需要更直观的命名,可以手动实现投影:
struct GetFirstElement { template<typename Tuple> auto operator()(const Tuple& tuple) const { return std::get<0>(tuple); } }; // 使用方式 std::ranges::sort(std::ranges::views::zip(x_values, y_values), std::less<>(), GetFirstElement{});
3. 不推荐:用std::bind_front绑定std::get<0>
这种方式需要显式指定tuple类型,代码繁琐,仅作参考:
using ZipElement = decltype(std::ranges::views::zip(x_values, y_values)[0]); std::ranges::sort(std::ranges::views::zip(x_values, y_values), std::less<>(), std::bind_front(std::get<0, ZipElement>));
为什么示例(3)无法编译?
&std::get<0>试图取函数模板的地址,但函数模板本身没有固定的实例化版本,编译器无法推导需要生成哪个具体函数,因此编译失败。而std::ranges::get<0>是专门为range场景设计的投影适配器,是一个完整的函数对象,而非普通函数模板,所以可以直接传递。
内容的提问来源于stack exchange,提问作者Damir Tenishev
相关产品推荐
相关产品推荐

