C++使用auto声明lambda变量触发std::bad_function_call异常问题咨询
问题原因分析
- 每个lambda表达式在编译期都会生成一个独有的匿名类类型,不是
std::function<void()>类型。当你用auto声明lambda变量时,变量的类型就是这个匿名类类型,而非std::function。 - 你的类A构造函数接收的是
const std::function<void()>&类型参数,当你传入auto声明的lambda时,编译器会隐式生成一个临时的std::function<void()>对象来匹配参数类型。这个临时对象的生命周期仅持续到构造函数调用语句结束,构造函数中你将这个临时对象的地址存入成员指针m_Lambda,构造完成后临时对象被销毁,m_Lambda变成悬垂指针,调用ExecuteLambda解引用悬垂指针就会抛出std::bad_function_call异常。 - 当你把lambda变量显式声明为
const std::function<void()>类型时,变量本身就是生命周期和main函数同步的左值对象,构造函数取的是这个有效对象的地址,因此运行正常。
可使用
auto声明lambda的场景 满足以下条件的场景都可以用auto声明lambda变量:
- 直接传递lambda给函数模板(比如STL算法的回调参数),不需要将其转换为
std::function长期持有 - 持有lambda本身的实例而非转换后的
std::function的指针/引用 - 不需要通过基类/类型擦除接口存储lambda的场景
保留
auto声明的修复方案 不需要修改lambda的auto声明,只需要调整类A的设计即可,核心是避免存储临时对象的地址:
方案1:类A直接存储std::function实例(推荐)
将成员变量从指针改为存储std::function实例,构造时直接转移lambda的所有权,不存在悬垂风险:
#include <iostream> #include <functional> class A { std::function<void()> m_Lambda; public: A(std::function<void()> lambda): m_Lambda(std::move(lambda)) {} void ExecuteLambda() { m_Lambda(); } }; int main() // 标准C++要求main返回int,void是MSVC扩展 { int i1 = 1; int i2 = 2; const auto lambda = [&]() { std::cout << "i1 == " << i1 << std::endl; std::cout << "i2 == " << i2 << std::endl; }; A a(lambda); a.ExecuteLambda(); return 0; }
方案2:模板化类A直接存储lambda类型
如果不想引入std::function的类型擦除开销,可以将类A模板化,直接存储lambda的实例:
template<typename Func> class A { Func m_Lambda; public: A(Func lambda): m_Lambda(std::move(lambda)) {} void ExecuteLambda() { m_Lambda(); } }; // C++17及以上支持类模板推导,可直接传入auto声明的lambda构造对象 A a(lambda);
内容的提问来源于stack exchange,提问作者user16415265
相关产品推荐
相关产品推荐

