【问题标题】:Why would a std::move of std::shared_ptr casue destruction为什么 std::move 的 std::shared_ptr 会导致破坏
【发布时间】:2020-05-04 16:48:07
【问题描述】:

我得到了一些类似这样的代码

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 一个函数返回到右值引用的值会导致破坏。不过直接放到一个右值引用就可以了。

问题实际上是在std::get&lt;&gt;std::tuple 中发现的。我有一个函数返回两个 shared_ptr 的元组并使用 std::get 来访问该元素。但我发现auto &amp;&amp; i = get&lt;0&gt;(f()) 会导致错误,但auto i = get&lt;0&gt;(f()) 不会。然后我为 std::move 找到了一个类似但更简单的情况

【问题讨论】:

  • 请不要问 XY 问题。如果您想使用std::tuple 查询某些std::get&lt;&gt; 的使用情况,请为此创建一个单独的问题。
  • @KamilCuk:这是一个合理的问题,有时会令人困惑。

标签: c++ reference move shared-ptr


【解决方案1】:
p && j = f(2);

这里,f 返回 std::shared_ptr&lt;A&gt; 类型的纯右值。当您将引用绑定到纯右值时,它会将纯右值的生命周期延长为引用的生命周期。这意味着f 返回的对象将与j 具有相同的生命周期。

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

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

【讨论】:

【解决方案2】:

让我们一一来看看:

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

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

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

这会使用f(2)返回的对象的引用来初始化引用j,这会延长f(2)返回的值的生命周期,所以这没关系。 j 的生命周期直到块结束。

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

这会使用对move返回的对象的引用来初始化引用,因为move返回的值是对f(2)返回的临时对象的引用(而不是临时对象),所以不会发生生命周期延长,但是f(2) 返回的临时值会在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 的生命周期。

【讨论】:

    【解决方案3】:

    因为通过使用std::move,您已经将返回值变成了引用。而不是 const 引用,所以它是临时的。

    所以没有复制发生,一旦行结束,返回值就被销毁了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-11
      • 2017-05-02
      • 2020-11-04
      • 1970-01-01
      • 2014-02-16
      • 1970-01-01
      • 2020-09-01
      相关资源
      最近更新 更多