dimanche 19 mars 2017

Behavioral differences with std::move(*this) from a member function

Given the class definition:

struct MoveOnly
{
    MoveOnly(int v_)
    : v(v_)
    {
        std::cout << ((void*)this) << " MoveOnly " << v << "\n";
    }

    ~MoveOnly()
    {
        std::cout << ((void*)this) << " ~MoveOnly " << v << "\n";
    }

    MoveOnly(const MoveOnly&) = delete;
    MoveOnly& operator=(const MoveOnly&) = delete;

    MoveOnly(MoveOnly &&src)
    {
        v = std::exchange(src.v, -1);
        std::cout << ((void*)this) << " MoveOnly&& " << v << "\n";
    }

    MoveOnly& operator=(MoveOnly&&) = default;

    MoveOnly&& Apply()
    {
        std::cout << ((void*)this) << " Apply " << v << "\n";
        return std::move(*this);
    }

    int v;
};

The console shows for code:

auto m1 = MoveOnly(1);
m1.Apply();

> 0x7fff5fbff798 MoveOnly 1
> 0x7fff5fbff798 Apply 1
> 0x7fff5fbff798 ~MoveOnly 1

Now, if I change Apply to return a value instead of an r-value reference:

MoveOnly Apply()
{
    std::cout << ((void*)this) << " Apply " << v << "\n";
    return std::move(*this);
}

I see:

auto m1 = MoveOnly(1);
m1.Apply();

> 0x7fff5fbff798 MoveOnly 1
> 0x7fff5fbff798 Apply 1
> 0x7fff5fbff790 MoveOnly&& 1
> 0x7fff5fbff790 ~MoveOnly 1
> 0x7fff5fbff798 ~MoveOnly -1

The first example seems to preserve the original object, which goes against my intuition for what std::move does. Although since it doesn't invoke the move constructor, I can see how the original object would still have a 1.

What I'm trying to figure out here is how the C++ standard explains this behavior and what the uses cases for the different version of Apply() might be.

Aucun commentaire:

Enregistrer un commentaire