c++emplaceaggregate-initialization

Aggregate initialization inside emplace_back


In the following code, I am trying to use aggregate initialization with emplace_back. The catch here is that emplace_back takes the constructor arguments and constructs the object directly in the vector, and I want to know whether the copy constructor is called on a A{1,2,3} or not. However, for aggregate initialization to work, the struct should not have a user-defined constructor. Any ideas what's happening behind the scenes?

#include <iostream>
#include <vector>

struct A {
    int x, y, z;
    /*A(int x, int y, int z) : x(x), y(y), z(z) 
    {
        std::cout << "ctor called\n";
    }
    A(const A& other) : x(other.x), y(other.y), z(other.z) {
        std::cout << "copy ctor called\n";
    }*/
};

int main() {

    std::vector<A> vec;

    vec.emplace_back(A{1, 2, 3});

    return 0;
}

Solution

  • emplace_back will construct the object directly in the vector, as you've said. However, you've already constructed a temporary object A{1, 2, 3} at the call site, so that object in the vector will be copy/move constructed, and there are two objects in total. Note that:

    This expression A{1, 2, 3} is a prvalue, and will be passed by rvalue reference to emplace_back. Then, the implicitly-defined move constructor of A is going to be called, not the copy constructor.

    Providing a copy constructor undeclares the move constructor

    If you uncomment the copy constructor in your code, then the copy constructor is called because the move constructor is not declared. You would have to add:

    A(A&&) = default;
    

    Then, the move constructor will be called, not the copy constructor. Furthermore, defining this constructor makes the type non-aggregate, so you now need to define a constructor A(int, int, int) because aggregate initialization is no longer possible.

    Note on C++20

    In C++20, you can initialize aggregates using parentheses as well. This allows you to use emplace_back as if A had a constructor A(int, int, int):

    struct A {
        int x, y, z;
    };
    
    std::vector<A> vec;
    vec.emplace_back(1, 2, 3);