0

I have following c++ code. You can see I created a struct with a constructor and a copy constructor. Can someone explain me why copy constructor is being invoked for the first assignment and not for the other 2 assignments?

#include <iostream>
#include <string>
#include <vector>

struct Vertex
{
    float x, y, z;
    Vertex(float x, float y, float z)
        : x(x), y(y), z(z)
    {
        std::cout << "Created Vertex!" << std::endl;
    }
    Vertex(const Vertex& v) // Copy constructor
        : x(v.x), y(v.y), z(v.z)
    {
        std::cout << "Copied!" << std::endl;
    }
};

std::ostream& operator<<(std::ostream& stream, const Vertex& _v) // Overloading operator<<
{
    stream << _v.x << ", " << _v.y << ", " << _v.z;
    return stream;
}

int main()
{
    std::vector<Vertex> vertices;
    vertices.reserve(3); 
    vertices.emplace_back(Vertex(1, 2, 3)); // 1st assignment
    vertices.emplace_back(4, 5, 6);
    vertices.emplace_back(7, 8, 9);

    return 0;
}
  • 2
    What do you expect `emplace_back` to do when you pass it a `Vertex` object? It must construct a `Vertex` inside the vector somehow from the argument. What else than the copy constructor should it use? (These are also no assignments. `emplace_back` creates new elements.) – user17732522 Aug 11 '22 at 07:45
  • 1
    Add Vertex move constructor. – 273K Aug 11 '22 at 07:47

2 Answers2

5

From the the reference for emplace_back

Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container.

As you can see in the example on the c++ reference page, emplace_back can be used to avoid copy and move constructors of the object to be created (though, the parameters of these constructors are still moved or copied --- in your case, being such parameters PODs, no copy/move of parameters is present).

But in your first call, you create the object by calling explicitly Vertex(1,2,3). Then you pass this variable to emplace_back to create a new object. Generally, for this purpose you would prefer to "move" the object to the vector, that is, to use the move constructor. But, since you have defined a copy construct, an implicitly-declared move constructor is not available. Hence the compiler has to fall back to your copy constructor to insert the element Vertex(1,2,3) into the vector.

francesco
  • 7,189
  • 7
  • 22
  • 49
  • nitpick: "... emplace_back can be used to avoid copy and move operations." It avoids to move the element, but the parameters forwarded to the constructor are still moved/copied – 463035818_is_not_an_ai Aug 11 '22 at 08:36
  • @463035818_is_not_a_number sure, added your comment. – francesco Aug 11 '22 at 09:41
  • @cpp_newbie If this or another answer suits your question, you should consider to [accept it](https://stackoverflow.com/help/someone-answers). There is no obligation to do that. – francesco Aug 12 '22 at 10:56
2

The argument(s) you give when you call emplace_back are forwarded to the relevant constructor for the type of object that the vector contains. In your first call, the argument you give is Vertex(1,2,3); that makes an explicit call to the constructor takings three float arguments, then passes the constructed object as the actual argument to emplace_back. Thus, the relevant constructor to be called is the copy constructor.

In the other two calls, the arguments given correspond to those for the 'plain' constructor, so that is the one chosen for those calls.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83