12

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.

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
Philipp
  • 957
  • 1
  • 6
  • 20
  • Why would it not be? C++ provides `cstdlib` for the purpose of providing the C type `malloc/calloc/realloc` functions. Whether you use `new/delete` or `malloc/free` is largely just a matter of what validation responsibility is put on you. – David C. Rankin Feb 09 '19 at 01:54
  • [utility - std::move](https://en.cppreference.com/w/cpp/utility/move) and [algorithm - std::move](https://en.cppreference.com/w/cpp/algorithm/move) provide for no particular allocation scheme. – David C. Rankin Feb 09 '19 at 02:02
  • 1
    Neat question. I think it probably hinges on how the move takes place. If object X frees a resource as part of the left hand side of a move assign and the resource is uninitialized because it was `malloc`ed and not constructed all manner of bad can happen. – user4581301 Feb 09 '19 at 02:10
  • 1
    Try any complex type for T for example `std::string` and you will see why using malloc is a bad practice. Placement new is a correct way but you must be responsible for calling of destructors. – 273K Feb 09 '19 at 02:19
  • That's what I thought. However, the first example would be right if it called the move constructor instead of the move assignment operator, right? – Philipp Feb 09 '19 at 02:22

1 Answers1

5

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.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • So it boils down to using the move constructor rather than the move assignment operator. Can the move constructor be invoked with placement new? Like so: new (next) T(std::move(*t)); – Philipp Feb 09 '19 at 02:43
  • 1
    @Philipp yes. All the same constructors can be used by placement new, as by allocating new, or variable definition. – eerorika Feb 09 '19 at 02:46