C++ priority_queue用lambda+decltype自定义比较器报未定义引用错误
问题根因
- 你在
node.h中定义的lambda比较器cmp属于匿名类型,C++标准中每个包含该头文件的翻译单元(也就是每个独立编译的.cpp文件),都会独立生成一个独有的lambda类型。哪怕代码完全一致,a.cpp中识别到的decltype(cmp)和main.cpp中识别到的decltype(cmp)是两个完全不同的类型。 - 这直接导致
a.cpp编译生成的plane::getNeighborNode函数签名,和main.cpp中调用时查找的函数签名不匹配:链接器在a.o目标文件里找不到main.o需要的那个参数类型的函数实现,就会报undefined reference错误。 - 替换为具名仿函数
compnode时,结构体类型有全局唯一的标识,所有翻译单元识别到的比较器类型完全一致,函数签名匹配,因此可以正常链接运行。 - 额外隐患:你当前直接在头文件里定义全局变量
cmp,就算解决了类型不一致问题,多个.cpp包含该头文件时还会触发全局变量重复定义的链接错误。
可行解决方案
按推荐优先级排序:
- 优先使用具名仿函数(你已经验证可用的方案)
这是兼容性最好、无额外规则陷阱的写法,所有C++版本都支持,也不会有ODR(单一定义规则)相关问题。注意不要在头文件里定义全局的仿函数实例,使用时直接构造即可:// node.h中定义仿函数 struct compnode { bool operator() (const node* a, const node* b) { // 填写你的节点比较逻辑 } }; // 声明优先级队列时直接指定仿函数类型,不需要额外传比较器实例 std::priority_queue<node*, std::vector<node*>, compnode> neighbors; // 类内函数声明、类外实现的模板参数和上面保持一致即可 - C++17及以上版本可使用inline lambda
C++17支持inline全局变量,保证所有翻译单元中同一个inline变量是同一个实体,对应的lambda类型也全局唯一,同时不会触发重复定义问题:
用这个方案需要在CMake中开启C++17支持:// node.h中定义inline的lambda比较器 inline auto cmp = [](const node* a,const node* b) { // 比较逻辑 };set(CMAKE_CXX_STANDARD 17) - 低版本C++可使用函数指针作为比较器类型
无捕获的lambda可以隐式转换为对应签名的函数指针,而函数指针类型是全局唯一的,不会有翻译单元类型不一致的问题:
这个方案的缺点是函数指针调用会有极轻微的性能损耗,且仅适用于无捕获的lambda。// 所有涉及priority_queue的声明、定义处,比较器模板参数统一写为函数指针类型 // 类中函数声明示例 void getNeighborNode(std::priority_queue<node*, std::vector<node*>, bool(*)(const node*, const node*)> & neighbors); // main.cpp中队列声明示例 std::priority_queue<node*, std::vector<node*>, bool(*)(const node*, const node*)> neighbors(cmp);
补充代码语法问题提示:你当前代码中class node、class plane的定义末尾都缺少分号,main.cpp中tmpplane没有看到实例化定义,这些也会导致编译失败,记得补上。
内容的提问来源于stack exchange,提问作者lichao
相关产品推荐
相关产品推荐

