You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何函数对象作为模板参数时左值与右值表现不同?

为什么直接传递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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 00:16:07