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

为何能在构造对象的Lambda中捕获未构造完成的对象?

为什么能捕获未构造的对象并读写?

先看这段代码:

int x = [&x](){
    std::cout << "Inside lambda, x is " << x << '\n';
    x = 5;
    return 23;
}();
std::cout << "x is " << x << '\n';

它能编译运行且无任何警告,哪怕lambda里捕获并读写了还没完成初始化的x。

如果你觉得这是因为int可以默认构造,那试试这段自定义类型的代码:

struct Int {
    int v;
    Int(int v):v{v}{}
};

static_assert(!std::is_constructible_v<Int>);
static_assert(std::is_constructible_v<Int, int>);

int main()
{
    Int x = [&x](){
        std::cout << "Inside lambda, x is " << x.v << '\n';
        x = 5;
        return 23;
    }();
    std::cout << "x is " << x.v << '\n';
}

这段代码同样能编译运行,这到底是怎么回事?


核心原因拆解

  1. 内存先分配,初始化后置
    声明变量x时,编译器第一步就会为它分配好内存空间,之后才会执行右边的初始化表达式(也就是调用lambda)。所以lambda里捕获的&x,指向的是一块已经存在的内存地址——这步操作本身是符合C++规则的,标准允许获取尚未完成构造的对象的地址。

  2. 这是标准明确的未定义行为
    别被“能编译运行”的表象迷惑,这段代码其实踩了C++的红线:

    • 对于int这类标量类型,读取未初始化的值属于未定义行为,编译器没有强制警告的义务;
    • 对于自定义的Int类型,在构造函数执行完毕(对象生命周期正式开始)之前,访问它的成员v、执行赋值操作,同样是未定义行为。

编译器没有报错或警告,是因为这种“初始化表达式里访问自身”的时序问题属于运行时逻辑范畴,编译器通常不会做这么深度的静态检查。现在能正常输出只是未定义行为的一种“幸运”表现,换个编译器、开启更高等级的优化或者换个运行环境,结果可能完全不可预料(比如输出垃圾值、程序崩溃)。


内容的提问来源于stack exchange,提问作者einpoklum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:04:54