详细分析一下为什么第一段代码是错的,第二段代码是对的,并解释 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(错误,应该是 boolbool(正确)
逻辑完整性存在分支没有 return(UB)所有分支都有 return
语义返回对象,没有表达顺序关系返回 true/false,明确表示 “谁应该在前”
是否满足 sort 要求❌ 不满足✅ 满足

✅ 一句话记忆

list.sort 的比较器必须是一个返回 bool 的函数,表达 “前一个参数是否应该排在后一个参数前面”,不能返回对象,也不能逻辑不完整。

Logo

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

更多推荐