存储Lambda的std::function相等性比较崩溃原因排查
咱们直接拆解核心问题:你的AreEqual函数处理lambda时崩溃,本质是解引用了空指针,这背后是std::function::target的类型匹配规则在起作用。
先看普通函数为什么能正常工作
当你把普通函数指针(比如&DummyFunc)赋值给std::function时,std::function内部存储的就是这个函数指针本身。此时调用function_1.template target<fnType*>(),因为请求的类型(普通函数指针fnType*)和存储的类型完全匹配,所以会返回一个指向该函数指针的有效指针,解引用后拿到函数地址做比较自然没问题。
为什么lambda会导致崩溃?
哪怕是无捕获的lambda,它的本质也是编译器生成的匿名 functor 类型,而非普通函数指针。当你把lambda赋值给std::function时,std::function存储的是这个匿名 functor 的实例,不是函数指针。
此时调用function_1.template target<fnType*>(),因为存储的类型(匿名 functor)和你请求的类型(普通函数指针)不匹配,target会返回nullptr。接下来你直接解引用*f_ptr_1,就触发了空指针解引用,程序自然崩溃。
补充:无捕获lambda确实可以隐式转换为函数指针,但这只是赋值时的临时转换——std::function并不会把它存储成函数指针,除非你显式转换后再赋值(比如FuncType1 f1 = +test_lambda;),但这只适用于无捕获lambda,有捕获的lambda连这个转换都做不到。
怎么修复这个问题?
要写出能正确处理所有可调用类型的AreEqual函数,需要先判断两个std::function的目标类型是否一致,再做安全的比较:
#include <typeinfo> #include <type_traits> template<typename T, typename... U> inline bool AreEqual(std::function<T(U...)> const& function_1, std::function<T(U...)> const& function_2) { // 先处理两个std::function都为空的情况 if (!function_1 && !function_2) { return true; } // 一个为空一个不为空,直接返回false if (!function_1 || !function_2) { return false; } // 目标类型不一致,直接返回false if (function_1.target_type() != function_2.target_type()) { return false; } // 处理普通函数指针的情况 using fn_ptr = T(*)(U...); if (auto f1_ptr = function_1.target<fn_ptr>()) { auto f2_ptr = function_2.target<fn_ptr>(); return *f1_ptr == *f2_ptr; } // 处理lambda/自定义functor的情况,依赖目标类型支持operator== using target_raw_type = typename std::decay_t<decltype(*function_1.target<void>())>; auto const* t1 = static_cast<const target_raw_type*>(function_1.target<void>()); auto const* t2 = static_cast<const target_raw_type*>(function_2.target<void>()); return *t1 == *t2; }
关键注意点:
- 上述代码依赖于目标可调用类型(比如lambda)支持
operator==:无捕获lambda编译器会默认生成该运算符;有捕获的lambda则需要所有捕获的变量都支持operator==才能正常比较。 - 如果你的编译器不支持C++17及以上,需要调整
std::decay_t的用法,或者用其他方式推导目标类型。
临时应急的测试用例调整
如果只是想让原有测试用例不崩溃(仅适用于无捕获lambda),可以显式把lambda转换成函数指针:
auto test_lambda = [](bool dummy_param) { dummy_param = true; }; FuncType1 f1 = +test_lambda; // 显式转换为函数指针 EXPECT_TRUE(AreEqual(f1, f1)); // 此时能正常通过
内容的提问来源于stack exchange,提问作者TheWaterProgrammer

