详细分析一下为什么第一段代码是错的,第二段代码是对的,并解释 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() 期望比较函数返回的是 booltrue 或 false),用来表示顺序。
  • 你这里返回的是 Person 对象,编译器会报错:
    • 因为 sort 的模板参数要求是一个返回 bool 的二元谓词(binary predicate)。
    • 返回 Person 根本无法直接判断 “谁排在前面”。
② 逻辑不完整
  • 你的比较逻辑中,如果 p1.age > p2.age,或者年龄相等但身高条件不满足,函数没有返回值
  • 对非 void 函数来说,控制流到达函数末尾却没有 return 是未定义行为(undefined behavior),有时会编译报错,有时会运行时崩溃或产生不可预期的结果。
③ 比较器的语义不对
  • 比较器的职责是告诉 sortp1 是否应该排在 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(年龄小的排前面)。
③ 符合严格弱序规则
  • 严格弱序要求:
    1. 不可反身性cmp(a,a) 必须返回 false
    2. 传递性:如果 cmp(a,b) 为 true 且 cmp(b,c) 为 true,则 cmp(a,c) 必须为 true
    3. 等价传递:如果 !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 的函数,表达 “前一个参数是否应该排在后一个参数前面”,不能返回对象,也不能逻辑不完整。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐