If memory is allocated with malloc
(as opposed to new
) and an object is moved into that memory, is that valid C++?
Let's say I allocate memory for an array of n objects of type T
, and I have a range of n objects of type T
that I want to move into that, is this valid:
T* next = (T *)malloc(n*sizeof(T));
T* t = std::begin(my_range);
while (we still have more Ts) {
*next = std::move(*t);
++next;
++t;
}
This seems to work, but I'm curious as to why it would since we never new the objects in the allocated memory that we move to.
My guess is placement new is the correct way to do it:
while (we still have more Ts) {
new (next) T(*t);
++next;
++t;
}
but I'd like to know WHY the first one is incorrect, and if so, if it just works by luck or because T
happens to be a POD.
If memory is allocated with malloc (as opposed to new) and an object is moved into that memory, is that valid C++?
Potentially; Not necessarily.
If we consider move-construction then sure, you can use placement-new to create an object into the memory. A bit like in your second example - except the example does a copy; unless the iterator is strange and returns an rvalue. Although, you mention the type being a POD in which case there is no difference between a move and a copy.
If we consider move assignment, then it is well defined only if there has previously been created an object into that memory. In your first example, no objects have been created, so the behaviour is technically undefined.
I'd like to know [...] if it just works by luck or because T happens to be a POD.
Technically UB, but is very likely to work if T
is POD. This type of initialisation by assignment is well defined in C (and the only way to create objects dynamically in that language).
If the assignment operator were non-trivial, then stuff would very likely break.
To move a range of objects into uninitialised memory, you'd probably want to use std::uninitialized_move
. It'll be well defined regardless of triviality of the type, and you don't even have to write a loop.