为什么 std::move of std::shared_ptr 会造成破坏

Why would a std::move of std::shared_ptr casue destruction

我有一些代码看起来像这样

struct A
{
    int i;
    A(int i) : i(i) {}
    ~A()
    {
        cout << "destroy " << i << endl;
    }
};
using p = shared_ptr<A>;
p f(int i)
{
    return make_shared<A>(i);
}
int main()
{
    auto i = f(1);
    cout << "a" << endl;
    p && j = f(2);
    cout << "a" << endl;
    p && k = move(f(3));
    cout << "a" << endl;
    auto l = move(f(4));
    cout << "a" << endl;
    p ptr = make_shared<A>(5);
    auto && a = move(ptr);
    cout << "a" << endl;
}

输出为

a
a
destroy 3
a
a
a
destroy 5
destroy 4
destroy 2
destroy 1

我不明白为什么 move 函数的 return 值指向右值引用会导致破坏。但是直接放到一个右值引用就可以了。

问题实际上是在 std::tuplestd::get<> 中发现的。我有一个函数 return 两个 shared_ptr 的元组并使用 std::get<> 访问元素。但是我发现 auto && i = get<0>(f()) 会导致错误,而 auto i = get<0>(f()) 不会。然后我发现 std::move

的情况类似但更简单

因为通过使用 std::move,您已将 return 值转换为参考。而不是 const 引用,所以它是临时的。

因此不会发生任何复制,该行一结束,return 值就会被销毁。

让我们一一了解这些:

auto i = f(1); // (1)

这将创建一个用 f(1) 的 return 值初始化的局部变量 i(不是引用)。还行吧。 i 的生命周期是直到块结束。

p && j = f(2); // (2)

这会使用对 f(2) 编辑的对象 return 的引用来初始化引用 j,这会延长 return 编辑的值 [=] 的生命周期20=],所以这没问题。 j 的生命周期是直到块结束。

p && k = move(f(3)); // (3)

这会使用对 move 编辑的对象 return 的引用来初始化引用,不会延长生命周期,因为 move 编辑的值 return 是一个引用由f(2)编辑的临时return(而不是临时对象),但是f(2)编辑的临时return一旦k 被初始化,因为它的生命周期没有延长(它没有分配给引用变量),并且 k 最终成为一个悬空引用。

auto l = move(f(4)); // (4)

同(1),此招无效。 l 的生命周期是直到块结束。

p ptr = make_shared<A>(5); // (5)

这是局部变量初始化。 ptr 的生命周期是直到块结束。

auto && a = move(ptr);

a 是对 ptr 的引用。这不会改变 ptr.

的生命周期
p && j = f(2);

这里,f return 是 std::shared_ptr<A> 类型的纯右值。当您将引用绑定到纯右值时,它会将纯右值的生命周期延长为引用的生命周期。这意味着由 f 编辑的对象 return 将具有与 j.

相同的生命周期
p && k = move(f(3));

在这里,再次 f return 是一个纯右值。 std::move 的参数绑定到该纯右值,将其生命周期延长至参数 的生命周期 std::move 不是 return 纯右值。它 return 是一个 xvalue。生命周期扩展不适用于 xvalues,因此 f 编辑的对象 return 在 std::move 的参数生命周期结束后立即被销毁。也就是说,它在 std::move return 时被销毁。 k 然后成为悬空引用。