Home > Net >  Unable to avoid copying while pushing objects with copy-construcor into a vector
Unable to avoid copying while pushing objects with copy-construcor into a vector

Time:10-18

I'm trying to avoid copying with emplace_back() and reserve(). But when I've tried to do it, i caught myself getting 3 copies for reason i cannot really understand. reserve() actually helps to avoid copying, but emplace_back() actually does nothing with it (works the same way as push_back() in this case). Here's the code:

struct Vertex
{
    size_t x, y, z;

    Vertex(size_t x, size_t y, size_t z)
    {
        this->x = x;
        this->y = y;
        this->z = z;
    }

    Vertex(const Vertex& v)
        : x(v.x), y(v.y), z(v.z)
    {
        std::cout << "\nCOPIED!\n";
    }
};

int main()
{
    std::vector<Vertex> vert;

    vert.reserve(3);
    vert.emplace_back(Vertex(1, 2, 3));
    vert.emplace_back(Vertex(4, 5, 6));
    vert.emplace_back(Vertex(7, 8, 9));

    return 0;
}

The output is 3 times 'COPIED!'. Well i tried to something like this:

    vert.emplace_back(std::move(Vertex(1, 2, 3)));
    vert.emplace_back(std::move(Vertex(4, 5, 6)));
    vert.emplace_back(std::move(Vertex(7, 8, 9)));

to convert my objects into r-values, but again i got 3 times 'COPIED!'.

Then i tried to push the same object 3 times with std::move and without and again got the same result:

    Vertex vertex(1, 2, 3);
    vert.emplace_back(vertex);
    vert.emplace_back(vertex);
    vert.emplace_back(vertex);

or

    Vertex vertex(1, 2, 3);
    vert.emplace_back(std::move(vertex));
    vert.emplace_back(std::move(vertex));
    vert.emplace_back(std::move(vertex));

I don't understand what am i doing wrong. I use MSVS 2022 Preview C 14. Also i tried to do it on C 14/17/20 and same result. Is there a way to get rid of all of the copies? Or maybe i understand situation wrong?

CodePudding user response:

std::move isn't useful here. The temporary Vertex object is already a prvalue, so casting it to an xvalue doesn't change anything. The class has no move constructor, so copy initialisation cannot move; it has to copy. The implicit move constructor has been inhibited by the user defined copy constructor. Although, the move constructor couldn't be any faster than the copy constructor for this class anyway.

The way that emplace_back works is that it forwards the arguments to the constructor of the element. If the argument that you pass is an object of the element type, then you invoke the constructor that accepts another instance of the class - that is the copy constructor (or the move constructor for classes that have it).

Instead of creating a temporary Vertex object, and passing it as an argument to emplace_back, you should pass the three size_t objects that can be forwarded to the constructor Vertex(size_t, size_t, size_t). This way you can avoid copying (and moving) of the Vertex object entirely:

vert.emplace_back(1, 2, 3);
vert.emplace_back(4, 5, 6);
vert.emplace_back(7, 8, 9);

CodePudding user response:

Since you provided a custom copy constructor for struct Vertex, no move constructor was automatically generated. Attempting to move this struct will use the copy constructor, as you observed.

Add a custom move constructor:

Vertex(Vertex &&v)
    : x(std::move(v.x)), y(std::move(v.y)), z(std::move(v.z))
{
    std::cout << "MOVED!\n";
}

But note that other than for printing, here you don't need neither custom copy nor move constructor. The implicitly generated ones should be enough.


std::move(Vertex(1, 2, 3))

std::move doesn't change anything here, because Vertex(1, 2, 3) is already an rvalue.


but emplace_back() .... works the same way as push_back() in this case

Yes. For it to make a difference, you need to pass constructor arguments directly into it, as mentioned in @eerorika's answer:

vert.emplace_back(1, 2, 3);

This will get rid of the copy/move, by constructing the object directly in the vector.

CodePudding user response:

Make it a POD (Plain Old Data) by adding a default constructor :

struct Vertex
{
    size_t x, y, z;

    Vertex() = default;

    Vertex(size_t xx, size_t yy, size_t zz) : x{xx}, y{yy}, z{zz} {}
};
  • Related