简体   繁体   中英

Can't launder(void), therefore free() is UB?

I am trying to determine what the strictly conforming pattern for using memory allocators with C++17 is. Specifically, moving away from the working in practice UB pattern:

example * foo = (example*)malloc(sizeof(example)); // no object here, don't use foo!

Therefore placement new into the allocated memory. Something like:

#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <new>

struct example { // alignment <= max_align_t
  example(int x) : x(x) {}
  ~example() {}
  int x;

  static void *operator new(size_t, example *p) { return p; }
};

int main() {

  example *ex = nullptr;

  {
    void *m = malloc(sizeof(example));
    if (!m) {
      return 1;
    }

    ex = new (reinterpret_cast<example *>(m)) example(42);

    // if one failed to store the result of the new expression, can retrieve it
    // from m via launder
    ex = std::launder(reinterpret_cast<example *>(m));

    // now abandon all further instances of 'void *m'
  }

// ... do some well defined things with the example object at *ex

All good, we have a properly constructed object. How do we free it?

// ...
  {
    ex->~example(); // lifetime has ended

    // UB? We don't have a void* to pass to free
    // free(ex);

    // equivalent to above, it's still not a void*
    // free(reinterpret_cast<void*>(ex));

    // error, new defines: void launder(void*) = delete
    // free(std::launder(reinterpret_cast<void*>(ex)));

    // error: void pointer argument to __builtin_launder is not allowed
    // free( __builtin_launder(reinterpret_cast<void*>(ex)));

    // Quoting the rule book, 21.6.4 [ptr.launder]
    // The program is ill-formed if T is a function type or cv void

    // Out of options, guess it has to be free
    free(ex);
  }
}

void * doesn't have the aliasing properties of a char* . Can't even do arithmetic on void*.

'Free' is a difficult thing to search for. I can't see special case magic for free, but even if there is such, it won't help with platform_allocate/platform_deallocate pairs.

On the basis that launder definitely doesn't permit one to get the void* back, free(ex) in the above is presumably the right thing to do.

What makes free(ex) well defined?

Why is launder(void) forbidden? A pointer optimisation barrier applied at the end of the malloc() implementation seems a great idea.

What makes free(ex) well defined?

free requires a pointer whose address is one directly returned by a call to malloc . You have provided that. Why wouldn't it work?

Why is launder(void) forbidden?

Because it makes no sense. std::launder<T> returns a pointer to an object of type T within its lifetime whose address is the address of the given pointer. void is an incomplete type; there cannot be objects of type void . Therefore, std::launder<void> cannot possibly function.

Memory allocation functions typically do not deal with pointers to objects. They deal in pointers to memory . The only thing they care about is whether the address of that pointer is one they can deal with.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM