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

为何std::unique_ptr与int*调用的生成代码差异大,无法优化delete?

为什么std::unique_ptr版本的代码无法优化到与裸指针版本一致?

两种函数定义与调用方式

接收std::unique_ptr的函数

void foo(std::unique_ptr<int>);
// 调用代码
foo(std::make_unique<int>(0));

接收裸指针的函数

void foo(int*);
// 调用代码
foo(new int{0});

预期与实际的汇编差异

我原本预期两者生成的汇编代码几乎完全相同:毕竟std::make_unique本质就是调用new int{0},后续的移动构造、临时对象销毁(此时已转移所有权,指针应为nullptr)都应该被优化掉,包括~unique_ptr()中的nullptr检查。

但实际生成的汇编差异显著:

std::unique_ptr版本的汇编(正常执行路径)

test():
        push    rbx
        sub     rsp, 16
        mov     edi, 4
        call    operator new(unsigned long)@PLT
        mov     dword ptr [rax], 0
        mov     qword ptr [rsp + 8], rax
        lea     rdi, [rsp + 8]
        call    foo(std::unique_ptr<int, std::default_delete<int>>)@PLT
        mov     rdi, qword ptr [rsp + 8]
        test    rdi, rdi
        je      .LBB0_3
        mov     esi, 4
        call    operator delete(void*, unsigned long)@PLT
.LBB0_3:
        add     rsp, 16
        pop     rbx
        ret

裸指针版本的优化后汇编

test():
        push    rax
        mov     edi, 4
        call    operator new(unsigned long)@PLT
        mov     dword ptr [rax], 0
        mov     rdi, rax
        pop     rax
        jmp     foo(int*)@PLT

当使用自定义空删除器std::unique_ptr<int, Nop>时,生成的代码符合预期(与裸指针版本几乎一致)。另外,将foo()声明为noexcept能减少部分生成代码,但仍会保留潜在的delete调用分支。

核心疑问

为什么优化器无法看穿所有权转移,从而优化掉多余的delete调用?我原本的预期执行流程是:

  • std::make_unique内部调用new int(0),若抛出异常则无需执行delete;
  • 参数被移动构造到foo中(或被返回值优化RVO省略),该操作是noexcept的;
  • foo执行完毕后会销毁自己的unique_ptr参数,接管所有权;
  • 原临时unique_ptr已转移所有权(指针为nullptr),其销毁时无需执行delete。

原因解析

这本质是因为优化器无法跨函数边界保证foo一定会正确接管所有权:

  • 虽然我们知道std::unique_ptr的移动构造会转移所有权、原对象变为nullptr,但优化器不能假设foo的实现一定会遵循规范——比如foo可能没有移动构造该unique_ptr,甚至可能手动将指针重置回原临时对象(这种行为不符合最佳实践,但优化器必须考虑所有合法的代码可能性);
  • 对于默认删除器std::default_delete<int>,优化器需要严格保证:如果临时unique_ptr销毁时指针非空,必须调用delete。由于无法确定foo是否会修改该指针的状态,所以必须保留nullptr检查和对应的delete分支;
  • 当使用自定义空删除器时,无论指针是否为空,unique_ptr销毁时都不会执行任何操作,优化器可以直接去掉所有多余分支,生成与裸指针一致的代码;
  • 标记foo为noexcept能减少异常处理相关的冗余代码,但依然无法消除对foo是否会修改指针状态的不确定性,因此delete分支无法被完全优化掉。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 20:13:15