调用std :: move返回值-签名应该是什么

时间:2018-11-29 02:03:50

标签: c++ c++11 move rvalue-reference

考虑

class X
{
public:
    std::unique_ptr<int> m_sp;
    A m_a;

    A test1()
    {
        return std::move(m_a);
    }

    A&& test2()
    {
        return std::move(m_a);
    } 

    std::unique_ptr<int> test3()
    {
        return std::move(m_sp);
    }

    std::unique_ptr<int>&& test4()
    {
        return std::move(m_sp);
    }

    std::unique_ptr<int> test5()
    {
        return std::make_unique<int>(50);
    }
};

class A
{
public:
    A()
    {
        m_i = 1;
    }

    A(A&& other)
    {
        this->m_i = other.m_i;
        other.m_i = -1;
    }

    A& operator=(A&& other)
    {
        this->m_i = other.m_i;
        other.m_i = -1;
        return *this;
    }

    int m_i;
};

参加这些课程

X x;
A y;
y.m_i = 10;
y = x.test1();

X x2;
A y2;
y2.m_i = 10;
y2 = x2.test2();

都调用A的move赋值,但仅在test1情况下才调用A的move构造函数。这是为什么?是因为我们无法返回A &&(std :: move会将A强制转换为A &&,但是test1说它必须返回A)。

通常,当您想转移/转让昂贵的成员变量的所有权时,是否要将返回值指定为右值引用(A &&)或左值(A)类型?

感觉好像有点不自然,好像您没有使用成员变量一样,您让RVO / NRVO做到了,只是返回一个左值。以unique_ptr为例,当您拥有一个自动变量时,您将拥有一个类似于test5()的签名,但是如果您拥有一个不适合RVO / NRVO的变量,例如一个成员变量,则应首选test3或test4的签名。

有兴趣知道。

谢谢

1 个答案:

答案 0 :(得分:6)

那里在语义上有所不同。当您返回类似

的对象时
A test1()
{
    return std::move(m_a);
}

std::unique_ptr<int> test3()
{
    return std::move(m_sp);
}

然后,您总是离开成员。无论调用方是否使用返回值执行任何操作,您都将移出X到临时目录。所有权不再与您同在。调用者可以接管返回值。如果调用方忽略返回值,则无论如何都将销毁该临时对象。另一方面,如果您返回右值引用,例如

A&& test2()
{
    return std::move(m_a);
}

std::unique_ptr<int>&& test4()
{
    return std::move(m_sp);
}

您只是为呼叫者提供了从对象的所有权转移/接管所有权的机会。如果呼叫者不执行移动,则您的X将保留所有权,该对象将不会移动。

要理解的关键是,与名称所暗示的相反,std::move()实际上并不执行移动。它仅允许移动给定的对象。实际的移动由相应类型的移动构造函数或移动赋值运算符执行。

因此,您的答案是:这取决于您要表达的内容。如果您返回一个对象,则是在说:“如果需要,我会把它扔掉:它在那儿”。如果您返回右值引用,则是说“就是这样,现在有机会接受它,否则我会保留它”……