代码之家  ›  专栏  ›  技术社区  ›  lz96

为什么无法消除这种独特的存储?

  •  3
  • lz96  · 技术社区  · 6 年前
    #include <memory>
    #include <vector>
    using namespace std;
    
    vector<unique_ptr<int>> e;
    
    void f(unique_ptr<int> u) {
        e.emplace_back(move(u));
    }
    

    两者皆有 Clang GCC ,上面的代码段生成如下内容:

    f(std::unique_ptr<int, std::default_delete<int> >):
            mov     rsi, QWORD PTR e[rip+8] # rsi: vector.end_ptr
            cmp     rsi, QWORD PTR e[rip+16] # [e + rip + 16]: vector.storage_end_ptr
            je      .L52 # Slow path, need to reallocate
            mov     rax, QWORD PTR [rdi] # rax: unique_ptr<int> u
            add     rsi, 8               # end_ptr += 8
            mov     QWORD PTR [rdi], 0   # <==== Do we need to set the argument u to null here? 
            mov     QWORD PTR [rsi-8], rax # *(end_ptr - 8) = u 
            mov     QWORD PTR e[rip+8], rsi # update end_ptr
            ret
    .L52:   # omitted
    

    我想知道为什么编译器会生成 mov QWORD PTR[rdi], 0

    此外,对于更简单的情况,如 this :

    void f(unique_ptr<int> u);
    
    void h(int x) {
        auto p = make_unique<int>(x);
        f(move(p));
    }
    

    为什么编译器会生成:

    call    operator delete(void*, unsigned long)
    

    h() ,假设p总是 nullptr 在调用 f ?

    1 回复  |  直到 6 年前
        1
  •  2
  •   Michael Kenzel    6 年前

    在这两种情况下,答案都是:因为从中移动的对象仍将被销毁。

    void f(unique_ptr<int> u);
    

    您将注意到,调用方为参数创建了对象 u f() 如果是内联的,编译器很可能会对此进行优化。但是生成的代码 无法控制的析构函数 因此,必须设置 假设 U 将在函数返回后运行。

    在你们的第二个例子中,我们有一种相反的情况:

    void h(int x) {
        auto p = make_unique<int>(x);
        f(move(p));
    }
    

    与名字可能暗示的相反, std::move() f() 就定义而言 h() ,它不能这样假设 f() 将始终从给定对象移动。例如 f() delete 对于析构函数。该函数还可以执行移动分配而不是移动构造,在这种情况下,仍然需要外部析构函数来释放以前由新对象的已分配所有权所持有的对象的所有权