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?
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.