mercredi 28 novembre 2018

Calling std::move on a return value - what should the signature be

Consider

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;
};

To exercise these classes

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

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

both call A's move assignment but only in the test1 case do we call A's move constructor. Why is that? Is it because as we cannot return a A&& (std::move will cast A to A&&, but test1 says it must return an A).

In general, when one wants to move/transfer ownership of expensive member variables, do you want to specify the return to be an rvalue-reference (A&&) or an lvalue (A) type?

It feels a little unnatural as if you aren't using member variables, you let RVO/NRVO do it's thing and just return an lvalue. Take in the case of unique_ptr, when you've got an automatic variable you have a signature like test5(), but if you have a variable, not suitable for RVO/NRVO, like a member varaible should test3 or test4's signature be preferred.

Interested to know.

Thanks

Aucun commentaire:

Enregistrer un commentaire