news 2026/5/4 11:21:42

告别std::sort的begin/end!C++20 ranges::sort实战:从基础排序到自定义规则

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
告别std::sort的begin/end!C++20 ranges::sort实战:从基础排序到自定义规则

告别std::sort的begin/end!C++20 ranges::sort实战:从基础排序到自定义规则

如果你已经用C++写过排序算法,一定对std::sortbegin/end迭代器对再熟悉不过了。每次写std::sort(vec.begin(), vec.end())时,有没有想过——这些重复的迭代器参数真的有必要吗?C++20的ranges::sort给出了答案:是时候告别这种冗余了。

1. 为什么需要ranges::sort?

传统std::sort的设计源于C++98时代,当时标准库的算法接口普遍采用迭代器对来表示范围。这种设计虽然灵活,但在日常使用中却带来了大量样板代码。根据GitHub代码分析,超过80%的std::sort调用都是对完整容器进行排序,这意味着我们每天都在重复编写相同的begin/end

C++20引入的Ranges库彻底改变了这一局面。ranges::sort的核心优势在于:

  • 直接操作容器:不再需要手动指定范围
  • 更安全的接口:编译时检查范围有效性
  • 更好的可组合性:可与视图(view)无缝配合
  • 更清晰的意图表达:代码更接近自然语言描述
// 传统方式 std::sort(vec.begin(), vec.end()); // C++20新方式 std::ranges::sort(vec);

2. 基础排序:从std::sort到ranges::sort的迁移

让我们看一个完整的迁移示例。假设我们有一个简单的整数排序需求:

#include <vector> #include <algorithm> int main() { std::vector<int> data = {5, 3, 1, 4, 2}; // 传统方式 std::sort(data.begin(), data.end()); // C++20方式 std::ranges::sort(data); return 0; }

虽然看起来只是省略了begin/end,但这背后代表着C++设计理念的重大转变。ranges::sort实际上接受的是一个range概念,而不仅仅是迭代器对。

2.1 范围安全性检查

ranges::sort在编译时会进行额外的范围检查,避免一些常见的迭代器错误:

std::vector<int> vec1 = {1, 2, 3}; std::vector<int> vec2 = {4, 5, 6}; // 潜在危险的传统方式 std::sort(vec1.begin(), vec2.end()); // 编译通过,运行时未定义行为 // 安全的C++20方式 std::ranges::sort(vec1, vec2.begin()); // 编译错误

3. 高级排序技巧

3.1 逆序排序

传统方式需要使用反向迭代器:

std::sort(vec.rbegin(), vec.rend());

ranges::sort可以直接使用标准比较器:

std::ranges::sort(vec, std::greater{});

3.2 自定义排序规则

对于复杂对象的排序,我们经常需要自定义比较函数。传统方式:

struct Person { std::string name; int age; }; bool compareByAge(const Person& a, const Person& b) { return a.age < b.age; } std::sort(people.begin(), people.end(), compareByAge);

使用ranges::sort可以更简洁:

std::ranges::sort(people, {}, &Person::age); // 按age升序 std::ranges::sort(people, std::greater{}, &Person::age); // 按age降序

3.3 投影(Projection)功能

ranges::sort最强大的特性之一是投影功能,它允许你在不修改元素的情况下对元素的某个属性进行排序:

std::vector<std::string> words = {"apple", "banana", "cherry"}; // 按字符串长度排序 std::ranges::sort(words, std::less{}, [](const auto& s) { return s.size(); }); // 等价于 std::ranges::sort(words, {}, &std::string::size);

4. 性能考量与最佳实践

虽然ranges::sort接口更现代化,但它的性能与传统std::sort相当。在大多数实现中,它们最终调用的是相同的底层排序算法。

最佳实践建议

  1. 尽早升级:如果项目已经使用C++20,优先使用ranges::sort
  2. 渐进式迁移:可以逐步替换代码中的std::sort
  3. 利用投影:善用投影功能减少临时对象的创建
  4. 注意约束:确保元素类型满足std::totally_ordered概念
// 编译时检查元素是否可排序 static_assert(std::ranges::sortable<std::vector<int>>);

5. 实际项目中的应用场景

5.1 数据库结果排序

struct Record { int id; std::string name; time_t timestamp; }; void sortRecords(std::vector<Record>& records, SortField field, SortOrder order) { switch(field) { case ID: std::ranges::sort(records, order == ASC ? std::less{} : std::greater{}, &Record::id); break; case NAME: std::ranges::sort(records, order == ASC ? std::less{} : std::greater{}, &Record::name); break; case TIMESTAMP: std::ranges::sort(records, order == ASC ? std::less{} : std::greater{}, &Record::timestamp); break; } }

5.2 游戏开发中的实体排序

struct GameObject { float x, y; int layer; bool operator<(const GameObject& other) const { return layer < other.layer; } }; void renderScene(std::span<GameObject> objects) { // 按层排序后渲染 std::ranges::sort(objects); for (const auto& obj : objects) { renderObject(obj); } }

6. 常见问题与解决方案

6.1 如何处理自定义比较器?

当需要复杂比较逻辑时,依然可以使用lambda表达式:

std::ranges::sort(employees, [](const auto& a, const auto& b) { if (a.department != b.department) return a.department < b.department; return a.salary > b.salary; });

6.2 如何对部分范围排序?

虽然ranges::sort不接受迭代器对,但可以使用views::dropviews::take

// 对前5个元素排序 std::ranges::sort(data | std::views::take(5)); // 跳过前3个元素,对其余排序 std::ranges::sort(data | std::views::drop(3));

6.3 兼容性考虑

如果项目需要同时支持C++20和旧标准,可以考虑使用条件编译:

#if __cplusplus >= 202002L std::ranges::sort(data); #else std::sort(data.begin(), data.end()); #endif

7. 与其他Ranges算法配合使用

ranges::sort可以与其他Ranges算法无缝配合,创建强大的数据处理管道:

// 过滤、转换然后排序 auto processed = data | std::views::filter([](int x) { return x % 2 == 0; }) | std::views::transform([](int x) { return x * 2; }); std::ranges::sort(processed);

这种组合方式不仅代码更简洁,而且通常比传统分步处理更高效,因为编译器可以进行更好的优化。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 11:20:21

ClawFleet实战:本地部署多AI智能体舰队,打造私有数字员工团队

1. 项目概述&#xff1a;打造你的本地AI“数字员工”舰队 如果你对AI Agent&#xff08;智能体&#xff09;感兴趣&#xff0c;并且不止满足于在云端调用API&#xff0c;而是想在自己的电脑上部署、管理和运行一个由多个AI智能体组成的“数字员工”团队&#xff0c;那么ClawFl…

作者头像 李华
网站建设 2026/5/4 11:09:26

终极解决方案:如何用OBS多平台推流插件实现一次编码多平台直播

终极解决方案&#xff1a;如何用OBS多平台推流插件实现一次编码多平台直播 【免费下载链接】obs-multi-rtmp OBS複数サイト同時配信プラグイン 项目地址: https://gitcode.com/gh_mirrors/ob/obs-multi-rtmp 如果你正在为同时向多个直播平台推流而烦恼&#xff0c;那么o…

作者头像 李华
网站建设 2026/5/4 11:08:37

从混乱到有序:NSC_BUILDER如何简化你的Switch游戏库管理

从混乱到有序&#xff1a;NSC_BUILDER如何简化你的Switch游戏库管理 【免费下载链接】NSC_BUILDER Nintendo Switch Cleaner and Builder. A batchfile, python and html script based in hacbuild and Nuts python libraries. Designed initially to erase titlerights encryp…

作者头像 李华
网站建设 2026/5/4 10:58:26

基于Axon.MCP.Server构建AI Agent工具:.NET 8实现MCP协议实战

1. 项目概述与核心价值最近在折腾AI Agent开发&#xff0c;特别是想给Claude Desktop或者Cursor这类工具加上自定义的“工具箱”&#xff0c;让它们能直接操作我的本地数据库、调用内部API&#xff0c;或者读取特定格式的日志文件。市面上通用的MCP&#xff08;Model Context P…

作者头像 李华
网站建设 2026/5/4 10:57:25

DeepPaperNote:基于AI的深度论文阅读笔记自动化工作流实践

1. 项目概述&#xff1a;从“读论文”到“建知识”的自动化桥梁 如果你和我一样&#xff0c;长期在科研、技术研发或者深度学习的道路上跋涉&#xff0c;那你一定对“论文消化不良”这个顽疾深有体会。我们花几个小时&#xff0c;甚至几天时间&#xff0c;精读一篇结构复杂、公…

作者头像 李华