Let's say I want to build a vector container that, unlike std::vector, allows uninitialized storage. The usage of the container, say vec <T>
, would be roughly like this:
User explicitly states the vector should allocate N uninitialized elements like that:
vec <T> a(N, no_init);
At some point when data are known, user explicitly initializes an element at position
n
using argumentsargs...
:a.init(n, args...);
OR, equivalently, constructs the element manually:
new (&a[n]) T(args...);
Other operations may initialize or copy more massively (like
std::uninitialized_copy
), but that's only for convenience; the basic underlying operation is the same.After completing some task, the vector may be left with some elements initialized and others not. The vector does not hold any extra information, so eventually, before releasing memory, it either destructs all elements anyway, or only destructs depending on
T
.
I am pretty sure this can be done, only I am not sure of the consequences. Naturally we'd like this structure to be safe for all types T
assuming the user does not attempt to use an uninitialized element before constructing it. This may sound like a strong assumption but accessing elements only within the vector's range is not so different an assumption and it's so common.
So my questions are:
For which types would it be safe to allow this kind of uninitialized operation as in
vec <T> a(no_init)
? I guessis_pod
would be ok and most probablyis_trivial
as well. I wouldn't like to put more constraints than necessary.Should destruction be performed always or only for some types? Would the same constraint be ok as above? How about
is_trivially_destructible
? The idea is that destructing an element that has not been constructed or vice versa (not destructing a constructed element) should do no harm.Is there a major flaw in this attempt, other than the apparent risk of putting more responsibility to the user?
The whole point is that when a user does need such functionality for performance, lower-level solutions like std::get_temporary_buffer
or manual allocation (e.g. with operator new()
) may be more risky in terms of leaking. I know about std::vector::emplace_back()
but that's really not the same thing.