c++pointersmemoryunique-ptrownership

How does the std::unique_ptr&& function argument work?


I'm writing a simple game engine in C++.

I'm very late to C++, because I've spent most of my free time with C, so the unique_ptr, shared_ptr and all of this ownership magic is hardly understandable for me.

I stumbled upon one thing that I can't understand at all. Let's have a following snippet:

#include <memory>
#include "vertexArray.hpp" // here VertexArray class is defined, it's implementation isn't important I think

class Mesh {
public:
    Mesh()= default;
    Mesh(std::unique_ptr<VertexArray>&& vertexArray) {
        // this->vertexArray = vertexArray; <- this doesn't work
        this->vertexArray = std::move(vertexArray); // Why is the move necessary?
    }
private:
    std::unique_ptr<VertexArray> vertexArray;
};

int main() {
    auto vArray = std::make_unique<VertexArray>(/* vertex buffer, index buffer, etc */);
    Mesh mesh = Mesh(std::move(vArray)); // <- I have to move the unique_ptr here, that I understand
}

I'm having a problem with this snippet, because I can't understand why the second std::move is necessary inside the Mesh::Mesh(std::unique_ptr<VertexArray>&&). From what I understand the assignment operator of std::unique_ptr expect rvalue at the right side. Isn't the vertexArray (which is type of std::unique_ptr<VertexArray>&&) already a rvalue?

Also, the std::move(vertexArray) isn't really clear to me, because in this example I guess the std::move will return nothing else then std::unique_ptr<VertexArray>&&, so isn't it returning exactly the same thing as vertexArray already is?

From what I understand, it is, so why do I have to call the std::move(vertexArray) where vertexArray is already a rvalue, to then convert it to... a rvalue?


Solution

  • Types and value categories are two independent things in C++.

    vertexArray is an lvalue-expression as it's the name of variable, even its type is rvalue-reference. You have to use std::move to convert it to rvalue-expression.

    The following expressions are lvalue expressions:

    • the name of a variable, ...

    std::move(vertexArray) is an xvalue-expression (rvalue-expression), as it's a function call whose return type is rvalue reference.

    The following expressions are xvalue expressions:

    • a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);