为何函数对象作为模板参数时左值与右值表现不同?
为什么直接传递lambda给AutoBrake会触发段错误,而先存为左值则正常?
在Arch Linux的GCC 13.2.1环境下,以下代码存在两种调用场景:
- Case 1:将lambda赋值给左值
f后再传入AutoBrake构造函数,程序运行正常 - Case 2:直接将lambda作为实参传入
AutoBrake构造函数,程序在回调访问brake_commands_published时触发段错误
代码示例(摘自《C++ Crash Course》):
#include <stdexcept> #include <stdio.h> constexpr void assert_that(bool statement, const char* message) { if (!statement) throw std::runtime_error{ message }; } struct SpeedUpdate { double velocity_mps; }; struct CarDetected { double distance_m; double velocity_mps; }; struct BrakeCommand { double time_to_collision_s; }; template <typename T> struct AutoBrake { AutoBrake(const T& publish) : collision_threshold_s{ 5 }, speed_mps{}, publish{ publish } {} void observe(const SpeedUpdate& su) { speed_mps = su.velocity_mps; } 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 }); } } void set_collision_threshold_s(double x) { if (x < 1) throw std::out_of_range{ "Collision less than 1." }; collision_threshold_s = x; } double get_collision_threshold_s() const { return collision_threshold_s; } double get_speed_mps() const { return speed_mps; } private: double collision_threshold_s; double speed_mps; const T& publish; }; void alert_when_collision_imminent() { int brake_commands_published{}; // case 1: no segfault /* auto f = [&brake_commands_published](const BrakeCommand&) { brake_commands_published++; }; AutoBrake auto_brake{ f }; */ // case 2: segfault /* AutoBrake auto_brake{ [&brake_commands_published](const BrakeCommand&) { brake_commands_published++; } }; */ auto_brake.set_collision_threshold_s(10L); auto_brake.observe(SpeedUpdate{ 100L }); auto_brake.observe(CarDetected{ 100L, 0L }); assert_that(brake_commands_published == 1, "brake commands published not one"); } int main() { alert_when_collision_imminent(); }
问题根源:临时对象的生命周期与悬空引用
- Case 2的问题:直接传入的lambda是一个临时对象,它的生命周期仅持续到
AutoBrake构造函数调用完成(即完整表达式结束)。而AutoBrake的成员publish是const T&类型的引用,绑定的正是这个临时lambda。构造函数执行完毕后,临时lambda被销毁,publish就变成了悬空引用。后续调用observe时通过这个悬空引用调用lambda,访问brake_commands_published自然会触发段错误。 - Case 1的正常逻辑:先将lambda赋值给局部左值
f,f的生命周期和AutoBrake对象auto_brake一致(都持续到alert_when_collision_imminent函数结束)。AutoBrake的成员引用publish绑定的是f,整个过程中引用始终有效,不会出现悬空问题。
补充说明
C++中虽然允许将临时对象绑定到const引用以延长其生命周期,但这种延长只适用于直接绑定的场景。这里AutoBrake的成员引用是间接绑定到构造函数的参数引用,而非直接绑定临时对象,因此临时对象的生命周期不会被延长到成员引用的生命周期,最终导致悬空引用。
内容的提问来源于stack exchange,提问作者Pedro Vinícius
相关产品推荐
相关产品推荐

