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?
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);