7

Given the simple code

#include <array>
#include <vector>
int main() {
    std::vector<std::array<int,3>> v;
    v.emplace_back(std::array<int,3>{1,2,3});
}

I'm first of all worried about what is really going on.

My understanding of emplace_back is that it constructs an element (in this case an std::array<int,3> with values 1, 2, 3) in-place by forwarding its argument(s) to that element's constructor.

But which constructor? Since we are passing an object of the same type of the element we want to emplace, I guess the move constructor is selected, as we're passing a temporary, or the copy ctor if the move ctor is deleted (implicitly or explicitly). Besides, we are first of all constructing that temporary, and that's not done in place, right?

So we are basically calling the "normal" (???) constructor of std::array<int,3> by passing to it 1, 2 , and 3 as arguments, thus getting back a temporary, which is passed to the copy ctor of std::array<int,3> which constructs the copy in place or, preferably, to the move ctor of std::array<int,3> which (?) copies the underlying C-style pointer?

It should be clear this simple example is confusing me a lot.

To confuse me even more is the doubt that if I really wanted to take advantage of emplace_back, I could pass to it only the arguments to the std::array<int,3>, as in

#include <array>
#include <vector>
int main() {
    std::vector<std::array<int,3>> v;
    v.emplace_back(1,2,3);
}

which doesn't even compile.

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • `std::array` has no user-defined constructors. It uses Aggregate Initialization. – user4581301 Nov 19 '20 at 23:20
  • Duplicate of https://stackoverflow.com/q/43394841/103167 (both the "struct" in the other question and your `std::array` are aggregates requiring aggregate initialization). – Ben Voigt Nov 19 '20 at 23:21

1 Answers1

10

Let me try to clarify a bit. First of, std::array<> is an aggregate. It does not have any user-defined constructors at all.

So it is quasi-movable (technical term is trivially movable), meaning you can construct it from temporary, but it is going to simply copy the underlying array. So what the code ends up doing is copying supplied array into the newly allocated vector element.

You would achieve identical results (but will do slightly less typing) if you replace the emplace_back with push_back like following:

    v.push_back({1,2,3});

And emplace_back(1, 2, 3) doesn't compile because std::array<> does not have any constructors which take more than one argument.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Why is a whole copy needed when using a temporary `std::array` to construct a `std::array`? Couldn't the pointer be stolen from the temporary to the object being created? – Enlico Nov 19 '20 at 23:36
  • 3
    @Enrico `std::array` does not have a stoleable pointer, because it does not manage a dynamically allocated memory. This is the main difference between `std::array<>` and `std::vector<>`. – SergeyA Nov 19 '20 at 23:38
  • Oh, sure! It's just that `std::vector` has a `T * ptr;` in it, `ptr` being a modifiable lvalue, whereas `std::array` has a `T arr[N];` in it, which is a non-modifiable lvalue. – Enlico Nov 20 '20 at 08:26
  • In C++20 ```emplace_back``` can initialize aggregates, but still cannot initialize std::array, so they are fundamentally differ. – Fedor Jun 21 '21 at 13:18
  • @Fedor can you please clarify your comment? – SergeyA Jun 21 '21 at 15:44
  • In C++20 one can use ```emplace_back``` for aggregates without any user ctor, but still not for ```std::array```. So we cannot say that aggregates and ```std::array``` behave similar in C++20 any more. – Fedor Jun 22 '21 at 09:45
  • @Fedor, I think, you are confused. https://en.cppreference.com/w/cpp/container/array - "This container is an aggregate type with the same semantics as a struct holding a C-style array..." – SergeyA Jun 22 '21 at 13:50
  • I just say that ```emplace_back``` shall work for aggregates in C++20: https://stackoverflow.com/questions/68006844/why-does-clang-12-refuse-to-initialize-aggregates-in-the-c20-way – Fedor Jun 22 '21 at 15:48