`operator delete` with size parameter and without size parameter: which one is chosen when both are available?
Asked Answered
M

1

7

When I run this code sample in GCC and Clang

struct S
{
  int a;        

  void *operator new(size_t s) 
  { 
    std::cout << "new " << s << std::endl;
    return ::operator new(s); 
  }

  void operator delete(void *p, size_t s) noexcept 
  { 
    std::cout << "delete " << s << std::endl;
    ::operator delete(p);
  }

  void operator delete(void *p) noexcept
  { 
    std::cout << "delete " << "none" << std::endl;
    ::operator delete(p);
  }
};

int main()
{
  S *p = new S;
  delete p;
}

I get the following output from both GCC and Clang

new 4
delete none

which means that the compilers selected the "sizeless" version of operator delete.

However, if I try something similar with globally replaced operator new and operator delete functions

struct S
{
  int a;        
};

void *operator new(size_t s)
{
  std::cout << "new " << s << std::endl;
  return std::malloc(s);
}

void operator delete(void *p, size_t s) noexcept
{
  std::cout << "delete " << s << std::endl;
  std::free(p);
}

void operator delete(void *p) noexcept
{
  std::cout << "delete " << "none" << std::endl;
  std::free(p);
}

int main()
{
  S *p = new S;
  delete p;
}

From GCC I get

new 4
delete 4

and from Clang I get

new 4
delete none

I know that "sized" version of in-class operator delete has been present in C++ since C++98, but looking through C++98 I don't seem to be able to find a definitive answer to the question of what version of operator delete should be selected in the first example. Is it even specified?

And what about C++14 and its "sized" version of global operator delete in the second example? Does the language say which version should be selected?

Maller answered 29/5, 2019 at 21:30 Comment(0)
B
8

This is CWG issue 255, dating back to the year 2000. To quote its premise:

Paragraph 4 of 15.5 [class.free] speaks of looking up a deallocation function. While it is an error if a placement deallocation function alone is found by this lookup, there seems to be an assumption that a placement deallocation function and a usual deallocation function can both be declared in a given class scope without creating an ambiguity. The normal mechanism by which ambiguity is avoided when functions of the same name are declared in the same scope is overload resolution; however, there is no mention of overload resolution in the description of the lookup. In fact, there appears to be nothing in the current wording that handles this case. That is, the following example appears to be ill-formed, according to the current wording:

struct S {
    void operator delete(void*);
    void operator delete(void*, int);
};
void f(S* p) {
    delete p;    // ill-formed: ambiguous operator delete
}

The status of the issue is currently "drafting", and as of writing this answer it appears still unresolved. There is no wording about overload resolution with regard to the deallocation functions.

Clang and GCC seem to be choosing arbitrarily. I'd argue it would have been better to emit some sort of diagnostic about the operator being ambiguous.

Boehm answered 29/5, 2019 at 21:38 Comment(3)
It is not related to operator delete[]?Sakmar
@Sakmar - No. operator delete has a legal overload that accepts the object size of the most derived object as an additional argument.Boehm
@Sandburg: It is related to both versions of the operator.Maller

© 2022 - 2024 — McMap. All rights reserved.