I'm trying to understand whether it's valid to call std::free()
on the result of placement new
in a buffer allocated by std::malloc()
.
Consider the following. Does this code exhibit any undefined behavior?
(Assume that T
does not overload any new
or delete
operators)
#include <cstdlib>
T* create() {
void* buf = std::malloc(sizeof(T) + 10);
T* obj = new(buf) T;
return obj;
}
int main() {
T* obj = create();
obj->~T();
std::free(obj); // or std::free(static_cast<void*>(obj)
}
Can the pointer returned by placement new
on a malloc()
'ed buffer then be passed to free()
? This should be well-defined if we can guarantee that static_cast<void*>(obj) == buf
, but I'm not sure if that equality is actually guaranteed; the placement new
operator returns its argument unchanged, but I'm not sure if we're allowed to assume that the placement new
expression returns a pointer with the same value/to the same address.
If this isn't defined, is there any other way to get a pointer that can be used to both access the object and free it, without encountering undefined behavior?
To answer my own question, cppreference states that std::construct_at(T* location, Args&&... args)
is equivalent to return ::new (voidify (*location)) T(std::forward<Args>(args)...);
(for non-array types) and that it returns location
. In other words, it is asserting that the return value of the non-allocating placement new expression is the same as the pointer passed in. The actual standard doesn't explicitly call out that the return value is location
from what I could see, but I trust cppreference enough to believe that it's implied by other parts of the standard, as other answers have stated.