Home > Net >  Why does gcc use the size-aware delete operator by default when optimizing?
Why does gcc use the size-aware delete operator by default when optimizing?

Time:05-19

If I define my own new and delete operators as shown below:

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

void* operator new (size_t count)
{
    printf("Calling custom new!\n");
    return malloc(count);
}

void operator delete(void *p) noexcept
{ printf("Called size unaware delete!\n");
  free(p);
}

int main()
{
    int *a = new int{1};
    delete a;
}

and compile using gcc version 12.1, with -O2 -Wall options specified, I get a mismatched-new-delete warning. Looking at the compiled output, I see that the compiler uses the size-aware delete operator (signature void operator delete(void *p, std::size_t sz);) instead of the custom one I defined (see compiler explorer output for details).

Other compilers such as clang use the delete operator I defined, and thus do not result in that mismatched operator warning. Why does gcc use the size-aware version when optimizing?

CodePudding user response:

References are to the post-C 20 draft (n4861). I am also assuming C 14 or later, which introduced size-aware deallocation functions.

For your particular example, the delete expression is required to call the size-aware operator delete, since the type to be destroyed is complete. So GCC is behaving correctly. (see [expr.delete]/10.5)

It is however not a problem that the size-aware one is chosen, because the default behavior of the global operator delete(void*, size_t) overload, if not replaced, is to just call the corresponding operator delete(void*), so your custom implementation will still be used in the end. (see [new.delete.single]/16)

There is however a recommendation in [new.delete.single]/11 that a program which replaces the operator delete version without size_t parameter should also replace the one with the size_t parameter. A note clarifies that although currently the standard library supplied default behavior of the size-aware versions call the custom non-size-aware implementation anyway, that may change in future standard revisions.

Also, the compiler is allowed to elide both the call to operator new and operator delete in the given example to either provide storage in a different manner or more likely to just elide the whole body of main which has no other observable side effects. So it is possible that no operator delete is called at all.

So, to future-proof the code and avoid linter warnings add a replacement for the size-aware global overload as well

void operator delete(void *p, std::size_t) noexcept
{
    ::operator delete(p);
}

Also note that the standard requires the replacement of this overload to behave in such a way that it could always be replaced by a call to the size-unaware version without affecting memory allocation. (see [new.delete.single]/15)


Although required since C 14, Clang doesn't seem to enable size-aware deallocation functions yet by default. You need to add the -fsized-deallocation flag to enable them.

Some discussion of a patch enabling it by default seems to be going on here.


Also note that your implementation of operator new is broken. The throwing version of operator new is not allowed to return a null pointer. So you must check the return value of malloc and throw std::bad_alloc if it is null.

  • Related