Suppose I want to construct a vector<T>
of size N
, where N
is known beforehand (but not at compile time). Normally, I would call vector<T>::reserve(N)
once and then call vector<T>::push_back(...)
N
times to fill the vector. However, for some reason I need to insert the elements out of order, although I know at which index each element should be inserted at beforehand.
Is there a good way to ensure that the constructor of T
is called exactly N
times in the construction of the vector?
If malloc
does not initialize allocated memory, then raw arrays allow this kind of technique to work efficiently. However, the returned pointer is not moveable to vector
, and at any rate, allowing a vector
to steal memory from an malloc
call (or maybe something like span
with dynamically determined size) is not necessarily wise because it could allow class invariants of T
to be violated if we do not actually initialize for every valid index. I guess the initialization guarantee is probably the crux of the issue here.
A simple example of this type of problem is calculating an inverse permutation, when permutations are represented as vector<size_t> perm
representing the map i -> perm[i]
. The performance might not be a big issue here, but T
could also be a type much more expensive to construct than size_t
.
This question is closely related, although the answers all suggest a solution that requires additional constructor calls.
EDIT: Let's say that T
is a 500 byte POD type, and we need good cache performance in linear iteration over the vector, which is why storing T*
and permuting is undesirable.
It seems like STL containers are not very compatible with the goal of touching each constructed object only once because they want to guarantee initialization of each object.
Peter's suggestion of modification-after-initialization is a good one. If T
is endowed with a dummy constructor, we can use reserve(N)
and then emplace_back
with that dummy constructor N
times. Afterwards we can properly initialize each element in any order.
Perhaps:
struct T {
struct DummyConstructorMarker {};
T(DummyConstructorMarker) {}
char data[500];
};
vector<T> makeVector(vector<size_t>& initOrder, int N, /* other state */) {
vector<T> result; result.reserve(N);
for (int i = 0; i < N; i++) {
result.emplace_back(T::DummyConstructorMarker());
}
for (size_t index: initOrder) {
/* Assign to data stored at index */
}
return result;
}