c++STL中list 的排序时都会踩的坑
·
详细分析一下为什么第一段代码是错的,第二段代码是对的,并解释 list.sort() 的工作原理。
源码:
*
list练习题目
1,利用list将Person自定义数据类型进行排序,Person中属性有姓名、年龄、身高。
2,排序规则,按照年龄进行升序,如果年龄相同按照身高进行降序。
*/
class Person {
public:
string name;
int age;
double height;
public:
Person(string name, int age, double height) {
this->name = name;
this->age = age;
this->height = height;
}
};
//自定义排序 bug代码
//Person cmp2(const Person& p1, const Person& p2) {
// if (p1.age < p2.age) {
// return p1;
// }
// else if (p1.age == p2.age) {
// if (p1.height > p2.height) {
// return p1;
// }
// }
//}
//修改后的自定义排序
bool cmp2(const Person& p1, const Person& p2) {
if (p1.age == p2.age) {
return p1.height > p2.height;
}
else {
return p1.age < p2.age;
}
}
void test20() {
//初始化几个人
Person p1("张三", 11, 1.6);
Person p2("李四", 11, 1.9);
Person p3("王五", 21, 1.5);
Person p4("江桂东", 21, 1.7);
list<Person> lst;
lst.push_back(p1);
lst.push_back(p2);
lst.push_back(p3);
lst.push_back(p4);
cout << "排序前:" << endl;
for (Person p : lst) {
cout << p.age << " " << p.name << " " << p.height << endl;
}
cout << endl << endl;
lst.sort(cmp2);
cout << "排序后:" << endl;
for (Person p : lst) {
cout << p.age << " " << p.name << " " << p.height << endl;
}
}
1️⃣ 先回顾一下 list::sort 的用法
在 C++ STL 里,list 是一个双向链表,它有一个成员函数 sort,用来对链表进行排序。它的函数原型是:
cpp
运行
// 无参版本,默认按升序排序(需要元素类型支持 < 运算符)
void sort();
// 带比较器(自定义排序规则)的版本
template <typename Compare>
void sort(Compare comp);
这里的 Compare comp 必须是一个比较函数(或者函数对象),它接受两个参数(const T& a, const T& b),返回一个 bool 值:
- 如果
a应该排在b前面,返回true - 否则返回
false
这个比较函数必须满足严格弱序(strict weak ordering)规则,才能保证排序正确。
2️⃣ 你写的 “bug 代码” 为什么是错的?
cpp
运行
// ❌ bug 代码
Person cmp2(const Person& p1, const Person& p2) {
if (p1.age < p2.age) {
return p1;
}
else if (p1.age == p2.age) {
if (p1.height > p2.height) {
return p1;
}
}
}
错误原因:
① 返回类型错了
list.sort()期望比较函数返回的是bool(true或false),用来表示顺序。- 你这里返回的是
Person对象,编译器会报错:- 因为
sort的模板参数要求是一个返回bool的二元谓词(binary predicate)。 - 返回
Person根本无法直接判断 “谁排在前面”。
- 因为
② 逻辑不完整
- 你的比较逻辑中,如果
p1.age > p2.age,或者年龄相等但身高条件不满足,函数没有返回值。 - 对非
void函数来说,控制流到达函数末尾却没有return是未定义行为(undefined behavior),有时会编译报错,有时会运行时崩溃或产生不可预期的结果。
③ 比较器的语义不对
- 比较器的职责是告诉
sort:p1 是否应该排在 p2 前面。 - 它应该返回
true或false,而不是返回某个对象。 - 你返回
p1或p2对排序算法来说毫无意义。
3️⃣ 修改后的代码为什么是对的?
cpp
运行
// ✅ 正确代码
bool cmp2(const Person& p1, const Person& p2) {
if (p1.age == p2.age) {
// 年龄相同,按身高降序
return p1.height > p2.height;
}
else {
// 年龄不同,按年龄升序
return p1.age < p2.age;
}
}
正确原因:
① 返回类型是 bool
sort需要的就是一个返回布尔值的比较函数,这样它才能判断顺序。
② 逻辑完整
- 所有分支都有
return:- 年龄相等时:比较身高,返回
p1.height > p2.height(如果 p1 比 p2 高,返回 true,即 p1 排前面)。 - 年龄不等时:返回
p1.age < p2.age(年龄小的排前面)。
- 年龄相等时:比较身高,返回
③ 符合严格弱序规则
- 严格弱序要求:
- 不可反身性:
cmp(a,a)必须返回false - 传递性:如果
cmp(a,b)为 true 且cmp(b,c)为 true,则cmp(a,c)必须为 true - 等价传递:如果
!cmp(a,b)且!cmp(b,a),则 a 与 b 等价,这种等价性要可传递
- 不可反身性:
- 你的代码完全满足这些条件:
p1.age < p2.age不可能同时p2.age < p1.age,满足不可反身性。- 年龄和身高的比较都是可传递的。
4️⃣ list.sort(cmp) 是怎么用比较器的?
list.sort 内部是一个链表排序算法(通常是归并排序的变体),它会多次调用你给的 cmp 函数来决定两个元素的顺序。
伪代码逻辑类似:
cpp
运行
sort(Compare comp) {
for (每个需要比较的元素对 a 和 b) {
if (comp(a, b)) {
// a 应该排在 b 前面,保持/交换位置
} else {
// b 应该排在 a 前面
}
}
}
你的比较器 cmp2 返回 true 时,就意味着:
“把第一个参数(p1)放在第二个参数(p2)的前面。”
5️⃣ 举个例子,走一遍排序过程
假设数据是:
plaintext
张三, 11, 1.6
李四, 11, 1.9
王五, 21, 1.5
江桂东, 21, 1.7
调用 lst.sort(cmp2) 时,sort 会不断调用 cmp2(p, q) 来判断顺序。
cmp2(张三, 李四):年龄相等 → 比较身高 → 1.6 > 1.9? 否 → 返回 false → 李四排前cmp2(王五, 江桂东):年龄相等 → 1.5 > 1.7? 否 → 返回 false → 江桂东排前cmp2(张三, 王五):11 < 21 → 返回 true → 张三排前
最终排序结果:
plaintext
11 李四 1.9
11 张三 1.6
21 江桂东 1.7
21 王五 1.5
6️⃣ 总结对比
| 对比点 | bug 代码 | 正确代码 |
|---|---|---|
| 返回类型 | Person(错误,应该是 bool) |
bool(正确) |
| 逻辑完整性 | 存在分支没有 return(UB) |
所有分支都有 return |
| 语义 | 返回对象,没有表达顺序关系 | 返回 true/false,明确表示 “谁应该在前” |
是否满足 sort 要求 |
❌ 不满足 | ✅ 满足 |
✅ 一句话记忆:
list.sort的比较器必须是一个返回bool的函数,表达 “前一个参数是否应该排在后一个参数前面”,不能返回对象,也不能逻辑不完整。
更多推荐


所有评论(0)