0

While researching ideas for a constexpr vector-like container, I ran into folly's small_vector container. I was reading through the implementation and was confused by this part of moveObjectsRightAndCreate:

    for (; in != first && out > lastConstructed;) {
      // Out must be decremented before an exception can be thrown so that
      // the rollback guard knows where to start.
      --out;
      new (out) T(std::move(*(--in)));
    }
    for (; in != first;) {
      --out;
      *out = std::move(*(--in));
    }
    for (; out > lastConstructed;) {
      --out;
      new (out) T(create());
    }
    for (; out != first;) {
      --out;
      *out = create();
    }

My understanding is this: For parts of the array where memory was uninitialized, they use placement new with std::move. For parts of the array where objects were previously moved from, they use move assignment.

It seems like the primary difference is whether the move constructor or move assignment is used, but I'm not really sure I understand the implications of these choices. Why not use placement new for both cases? Why not use move assignment for both cases? Since we know the size of the array, we will never call the destructor on uninitialized memory anyway, right?

ParadoxFox
  • 133
  • 1
  • 8
  • 2
    Are you asking why it doesn't just construct a new object on top of the same memory where an old object still exists? – Joseph Sible-Reinstate Monica Jan 01 '21 at 04:30
  • @JosephSible-ReinstateMonica Basically yeah. In this case, the old object should have been moved already due to previous placement new calls. Moreover, since we're manually managing memory, I feel like shouldn't have to worry about calling the destructor on uninitialized memory or some invalid object, but I'm probably misunderstanding something. – ParadoxFox Jan 01 '21 at 04:36
  • 1
    Hint: what will happen if you have a class that (1) manages a resource, and (2) is copyable but not movable (perhaps because it was written before move constructors existed)? – Joseph Sible-Reinstate Monica Jan 01 '21 at 04:38
  • @JosephSible-ReinstateMonica Ah okay, I think I see what you're getting at. For instance, if the class implements shared ownership of the resource, then you would expect the copy constructor to add a reference to the resource, but copy assignment should remove the old reference before adding the new one. I guess this is invalid in the same way mallocing memory and attempting copy assignment is invalid. – ParadoxFox Jan 01 '21 at 05:31

1 Answers1

2

Why not use placement new for both cases

Possibly because that would not allow them to specify that destructors of elements will not be called by insertion (in case where capacity doesn't grow). They don't seem to specify their API that precisely, but they might consider that to be implicitly expected.

Or they may assume or expect that assignment could in theory be more efficient for some type than destruction + construction.

Whether the actual reason is one of these, or something else, can only be answered by the author of the container.

Why not use move assignment for both cases?

Because the behaviour of move assignment of a non-trivial type on uninitialised memory is undefined.

eerorika
  • 232,697
  • 12
  • 197
  • 326