C++自定义对象优先级队列:比较器工作机制测试与疑问
最近我一直在琢磨C++里priority_queue的比较器工作机制,特意做了几个测试来验证,分享下我的发现:
测试1:自定义比较器类
直接使用priority_queue<T, vector<T>, cmp>的形式,这里的cmp是我们自定义的比较器类,这种方式运行一直很稳定。
示例代码:
// 自定义比较器类,实现小顶堆逻辑 struct cmp { bool operator()(const int& lhs, const int& rhs) { // 返回true时,lhs会被判定为优先级更低,被放在堆的下层 return lhs > rhs; } }; int main() { priority_queue<int, vector<int>, cmp> pq; pq.push(3); pq.push(1); pq.push(2); // 输出1,符合小顶堆预期 cout << pq.top() << endl; return 0; }
这种方式的优势是灵活性拉满——不管是基础类型还是自定义类型,都能通过定制cmp类来实现任意排序逻辑,编译器能明确识别比较规则,完全不会有歧义。
测试2:全局重载operator<
针对自定义结构体,在全局作用域重载operator<,然后直接使用priority_queue<test>,运行结果完全符合预期。
示例代码:
struct test { int a = 0; }; // 全局重载operator<,定义test的比较规则 bool operator<(const test& lhs, const test& rhs) { // 默认大顶堆逻辑:返回true意味着lhs优先级低于rhs,rhs会被放在堆顶 return lhs.a < rhs.a; } int main() { priority_queue<test> pq; pq.push({1}); pq.push({3}); pq.push({2}); // 输出3,符合大顶堆预期,a值最大的元素在堆顶 cout << pq.top().a << endl; return 0; }
这是因为priority_queue默认使用std::less<T>作为比较器,而std::less<T>会调用全局的operator<来判断元素优先级。如果自定义类型的比较规则是全局通用的,这种写法会让代码更简洁。
测试3:类内部的结构体与operator<重载(踩坑场景)
我尝试把测试2的内容放到一个外部类T里面,结果遇到了编译问题——原来如果把operator<定义为外部类T的成员函数,它会带有隐式的this指针,无法被std::less<T>识别调用。
错误写法示例:
class T{ public: struct test { int a = 0; }; // 错误:成员函数形式的operator<,带有隐式this指针,不符合std::less的调用要求 bool operator<(const test& lhs, const test& rhs) { return lhs.a < rhs.a; } }; int main() { priority_queue<T::test> pq; // 编译失败,找不到合适的operator< return 0; }
两种正确解决方式:
- 在内部结构体
test中重载operator<
class T{ public: struct test { int a = 0; // 成员函数形式的operator<,std::less会自动调用(左操作数是this) bool operator<(const test& rhs) const { return this->a < rhs.a; // 保持大顶堆逻辑 } }; }; int main() { priority_queue<T::test> pq; pq.push({1}); pq.push({3}); pq.push({2}); cout << pq.top().a << endl; // 输出3,正常运行 return 0; }
- 全局函数+友元声明
class T{ public: struct test { int a = 0; // 友元声明,让全局operator<可以访问内部成员(如果需要) friend bool operator<(const test& lhs, const test& rhs); }; }; // 全局作用域的operator< bool operator<(const T::test& lhs, const T::test& rhs) { return lhs.a < rhs.a; } int main() { priority_queue<T::test> pq; pq.push({1}); pq.push({3}); pq.push({2}); cout << pq.top().a << endl; // 输出3,正常运行 return 0; }
总结一下核心逻辑
priority_queue的优先级判断本质是依赖比较器的:
- 默认使用
std::less<T>,它会尝试调用operator<(全局函数或结构体内部的成员函数); - 自定义比较器类时,需要重载
operator(),明确写出两个元素的优先级判断逻辑; - 当自定义类型嵌套在类内部时,要注意
operator<的作用域和调用方式,避免隐式this指针带来的编译问题。
内容的提问来源于stack exchange,提问作者Seraph
相关产品推荐
相关产品推荐

