C++优先队列使用静态成员函数作为比较器触发AddressSanitizer错误
可复现代码
#include <bits/stdc++.h> using namespace std; class testClass { private: static bool comp(const int& a, const int& b) { return a > b; } priority_queue<int, vector<int>, decltype(&testClass::comp)> q; public: void push(int num) { q.push(num); } }; int main() { testClass t; t.push(0); t.push(0); return 0; }
问题描述
上述代码在第二次调用push时触发错误,推测priority_queue无法调用comp静态成员函数,但将该比较逻辑改为类外lambda并使用decltype(comp)作为比较器类型时,代码可正常运行。请问为何静态成员函数会引发此问题,而lambda方法却可行?
原因分析与解答
1. 静态成员函数作为比较器的问题根源
当你使用decltype(&testClass::comp)作为priority_queue的比较器类型时,这个类型本质是函数指针(bool(*)(const int&, const int&))。priority_queue要求比较器类型必须是可调用的,而函数指针类型的比较器需要你显式传递具体的函数指针值来初始化队列实例。
你的代码中priority_queue<int, vector<int>, decltype(&testClass::comp)> q;只是声明了队列,但没有给q传入&testClass::comp这个函数指针,此时q内部的比较器是一个默认构造的空函数指针。第一次push时队列只有一个元素,不需要执行比较逻辑;第二次push需要比较新元素和堆顶元素时,调用空指针就会触发错误。
修复方法也很简单:在类的构造函数中显式初始化队列,传入静态成员函数的指针:
class testClass { private: static bool comp(const int& a, const int& b) { return a > b; } priority_queue<int, vector<int>, decltype(&testClass::comp)> q; public: // 显式初始化队列,传入比较器指针 testClass() : q(&testClass::comp) {} void push(int num) { q.push(num); } };
2. 类外lambda可行的原因
类外无捕获的lambda有两个关键特性:
- 无捕获的lambda本身是一个仿函数类型(带有
operator()的类),而非单纯的函数指针。 - 在C++17及以后,无捕获lambda的闭包类型支持默认构造。当你用
decltype(comp)(lambda的闭包类型)作为priority_queue的比较器类型时,队列默认构造时会自动创建一个该闭包类型的实例,调用时直接执行lambda的operator(),不需要额外传递任何指针或实例,因此可以正常工作。
即使在C++17之前(lambda闭包类型不支持默认构造),只要显式用lambda实例初始化队列(比如priority_queue<int, vector<int>, decltype(comp)> q(comp);),同样可以正常运行,因为此时队列持有了一个有效的仿函数实例。
内容的提问来源于stack exchange,提问作者DanielRicky

