向Lambda函数传递引用触发段错误的原因及修复方案
段错误成因分析与修复方案
成因分析
你的推测方向正确,核心问题是AutoBrake持有了悬空的函数对象引用:
- 初始化
AutoBrake autoBrake{[...]}时,传入的是临时lambda表达式,这个临时对象在构造函数调用完成后会立即销毁。 - AutoBrake的成员变量
const T& publish保存的是该临时lambda的引用,lambda销毁后,publish就变成了悬空引用。 - 当
observe方法调用publish(BrakeCommand{...})时,实际是访问已经被销毁的lambda对象,直接触发段错误。 - 补充:lambda捕获的
brake_commands_published引用本身是有效的,但因为lambda先被销毁,所以本质问题还是publish引用悬空导致的非法访问。
修复方法
有两种可靠的修复方式,推荐第一种:
方式一:让AutoBrake持有函数对象的副本(最安全)
把AutoBrake中保存引用的成员改为保存值,让AutoBrake持有lambda的独立副本,彻底规避生命周期问题:
template<typename T> struct AutoBrake { // 构造函数接收const引用后拷贝到成员变量 AutoBrake(const T &publish) : publish{publish}, speed_mps{}, collision_threshold_s{5} {} // 也可以用移动语义优化性能: // AutoBrake(T publish) // : publish{std::move(publish)}, speed_mps{}, collision_threshold_s{5} {} void observe(const CarDetected &cd) { const auto relative_velocity_mps = speed_mps - cd.velocity_mps; const auto time_to_collision_s = cd.distance_m / relative_velocity_mps; if (time_to_collision_s > 0 && time_to_collision_s <= collision_threshold_s) { publish(BrakeCommand{time_to_collision_s}); } } private: T publish; // 从引用类型改为值类型 double collision_threshold_s; double speed_mps; };
方式二:延长lambda的生命周期(适合必须用引用的场景)
如果坚持使用引用,需要确保传入的lambda与AutoBrake对象生命周期一致。先将lambda赋值给局部变量,再传给AutoBrake:
void alert_when_imminent() { int brake_commands_published{}; // 将lambda存入局部变量,生命周期与autoBrake一致 auto publish_handler = [&](const BrakeCommand &) { brake_commands_published++; }; AutoBrake autoBrake{publish_handler}; // 后续代码保持不变 autoBrake.observe(CarDetected{100L, 0L}); assert_that(brake_commands_published == 1, "brake commands published not one"); }
内容的提问来源于stack exchange,提问作者Ikun
相关产品推荐
相关产品推荐

