1. 理解std::ranges的排序能力
C++20引入的std::ranges命名空间为算法库带来了革命性改进,其中排序功能尤为突出。传统std::sort需要明确指定迭代器范围,而ranges::sort可以直接操作容器或视图,代码更简洁。但真正强大的地方在于它支持比较器(comparator)和投影函数(projection)的组合使用。
1.1 基本排序示例
先看一个最简单的使用场景:
cpp复制std::vector<int> nums = {5, 3, 1, 4, 2};
std::ranges::sort(nums); // 升序排序
这行代码等价于传统的std::sort(nums.begin(), nums.end()),但语法更简洁。ranges版本会自动推导迭代器范围,减少了出错可能。
1.2 自定义比较器
当需要自定义排序规则时,可以传入比较函数:
cpp复制auto descending = [](int a, int b) { return a > b; };
std::ranges::sort(nums, descending);
比较器是一个可调用对象,接受两个参数,返回bool表示第一个参数是否应该排在第二个参数前面。这与传统std::sort的用法一致。
1.3 投影函数初探
投影函数(projection)是ranges算法的新特性,它允许在比较前对元素进行转换:
cpp复制struct Person {
std::string name;
int age;
};
std::vector<Person> people = {{"Alice", 25}, {"Bob", 20}, {"Charlie", 30}};
// 按年龄排序
std::ranges::sort(people, std::ranges::less{}, &Person::age);
这里的&Person::age就是投影函数,它告诉算法在比较时只关注age成员。相当于先对每个元素应用投影,再比较投影结果。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 比较器与投影的组合机制
2.1 执行顺序解析
当同时提供比较器和投影时,算法执行的逻辑顺序是:
- 对元素A应用投影函数,得到projA
- 对元素B应用投影函数,得到projB
- 调用比较器:comp(projA, projB)
这种设计使得我们可以分离"比较什么"和"怎么比较"两个关注点。
2.2 类型系统的影响
投影函数会影响比较器的参数类型。比较器实际接收的是投影结果的类型,而非原始元素类型。例如:
cpp复制std::vector<std::string> words = {"apple", "banana", "cherry"};
// 按长度排序,降序
std::ranges::sort(words,
[](size_t a, size_t b) { return a > b; }, // 比较器
[](const std::string& s) { return s.size(); }); // 投影
注意比较器的参数是size_t而非string,因为它比较的是投影结果(字符串长度)。
2.3 标准比较器适配
std::ranges提供了标准比较器对象:
- less:升序(默认)
- greater:降序
- less_equal
- greater_equal
- equal_to
- not_equal_to
这些可以与投影自由组合:
cpp复制// 按字符串长度升序
std::ranges::sort(words, std::ranges::less{},
[](const auto& s) { return s.size(); });
3. 实际应用场景
3.1 多字段排序
组合比较器和投影可以实现复杂排序逻辑。例如先按年龄升序,同年龄按姓名降序:
cpp复制std::ranges::sort(people,
[](const auto& a, const auto& b) {
return std::tie(a.age, b.name) < std::tie(b.age, a.name);
});
也可以分开使用投影:
cpp复制std::ranges::sort(people,
std::ranges::less{}, // 默认比较器
[](const Person& p) {
return std::tuple(p.age, std::ref(p.name)); // 投影多个字段
