为什么std :: move of std :: shared_ptr会被破坏

时间:2020-05-04 16:48:07

标签: c++ reference move shared-ptr

我有一些类似这样的代码

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<>中的std::tuple中发现了问题。我有一个函数,返回两个shared_ptr的元组,并使用std :: get <>访问该元素。但是我发现auto && i = get<0>(f())会导致错误,而auto i = get<0>(f())不会。然后我发现std :: move

的情况类似但更简单

3 个答案:

答案 0 :(得分:2)

让我们一一讲解

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

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

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

这将使用对j返回的对象的引用来初始化引用f(2),并延长了f(2)返回的值的寿命,因此可以。 j的生存期一直到块结束。

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

这将使用对move返回的对象的引用来初始化该引用,由于move返回的值是对f(2)返回的临时对象的引用,因此不会延长生存期。 (不是临时对象),但{strong> f(2)返回的临时变量在初始化k之后就死了,因为它的生命周期没有延长(未分配给引用变量),并且{{ 1}}最终成为悬而未决的参考。

k

与(1)一样,此举是无用的。 auto l = move(f(4)); // (4) 的生存期一直到块结束。

l

这是局部变量初始化。 p ptr = make_shared<A>(5); // (5) 的生存期一直到块结束。

ptr

auto && a = move(ptr); 是对a的引用。这不会更改ptr的生存期。

答案 1 :(得分:2)

p && j = f(2);

此处,f返回类型为std::shared_ptr<A>的prvalue。当您将引用绑定到prvalue时,它将把prvalue的生存期延长为引用的生存期。这意味着f返回的对象的生存期与j相同。

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

在这里,f再次返回prvalue。 std::move的参数绑定到该prvalue,将其生存期扩展为参数的生存期 std::move不会返回prvalue。它返回一个xvalue。生存期扩展名不适用于xvalues,因此f的参数生存期结束后,由std::move返回的对象将被销毁。也就是说,它在std::move返回时被销毁。 k然后成为悬空参考。

答案 2 :(得分:1)

由于使用std::move,您已将返回值转换为引用。而不是const引用,因此是临时的。

因此不会进行复制,并且在行结束时,返回值将被破坏。