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

Pro*_*rof 2 c++ move rvalue-reference c++11

考虑

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;
};
Run Code Online (Sandbox Code Playgroud)

练习这些课程

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

X x2;
A y2;
y2.m_i = 10;
y2 = x2.test2();
Run Code Online (Sandbox Code Playgroud)

两者都调用A的移动赋值,但只有在test1的情况下我们才调用A的移动构造函数.这是为什么?是因为我们不能返回A &&(std :: move会将A转换为A &&,但test1表示它必须返回A).

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

感觉有点不自然,好像你没有使用成员变量,你让RVO/NRVO做它的事情并且只返回一个左值.考虑到unique_ptr的情况,当你有一个自动变量时,你有一个像test5()这样的签名,但是如果你有一个变量,不适合RVO/NRVO,就像一个成员变量一样,应该首选test3或test4的签名.

有兴趣知道.

谢谢

Mic*_*zel 6

那里有语义差异.当你返回像中的对象时

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

std::unique_ptr<int> test3()
{
    return std::move(m_sp);
}
Run Code Online (Sandbox Code Playgroud)

那么你总是离开你的成员.无论来电者是否做了具有返回值的事情,您都会被移出X一个临时的.所有权不再与你同在.调用者可以接管返回值.如果调用者忽略返回值,则临时将被销毁.另一方面,如果你返回rvalue-reference,就像在

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

std::unique_ptr<int>&& test4()
{
    return std::move(m_sp);
}
Run Code Online (Sandbox Code Playgroud)

您只是为调用者提供了移动/接管对象所有权的机会.如果调用者未执行移动,您X将保留所有权,则不会移动该对象.

要理解的关键是,与名称相反,std::move()实际上并没有采取行动.它只允许移动给定的对象.实际移动由相应类型的移动构造函数或移动赋值运算符执行.

所以你的答案是:这取决于你想表达什么.如果你返回一个物体,你会说"如果你想要的话,我就把它扔掉了:它就在那边".如果你返回一个右值参考,你会说"那就是事情,现在你有机会接受它,否则我会保留它"......