c++c++14language-lawyer

free() on result of placement new?


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?


Solution

  • 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.

    https://en.cppreference.com/w/cpp/memory/construct_at