15 Special member functions [special]

15.5 Free store [class.free]

Any allocation function for a class T is a static member (even if not explicitly declared static).

[Example:

class Arena;
struct B {
  void* operator new(std::size_t, Arena*);
};
struct D1 : B {
};

Arena*  ap;
void foo(int i) {
  new (ap) D1;      // calls B​::​operator new(std​::​size_­t, Arena*)
  new D1[i];        // calls ​::​operator new[](std​::​size_­t)
  new D1;           // ill-formed: ​::​operator new(std​::​size_­t) hidden
}

end example]

When an object is deleted with a delete-expression, a deallocation function (operator delete() for non-array objects or operator delete[]() for arrays) is (implicitly) called to reclaim the storage occupied by the object ([basic.stc.dynamic.deallocation]).

Class-specific deallocation function lookup is a part of general deallocation function lookup ([expr.delete]) and occurs as follows. If the delete-expression is used to deallocate a class object whose static type has a virtual destructor, the deallocation function is the one selected at the point of definition of the dynamic type's virtual destructor ([class.dtor]).118 Otherwise, if the delete-expression is used to deallocate an object of class T or array thereof, the static and dynamic types of the object shall be identical and the deallocation function's name is looked up in the scope of T. If this lookup fails to find the name, general deallocation function lookup ([expr.delete]) continues. If the result of the lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function, the program is ill-formed.

Any deallocation function for a class X is a static member (even if not explicitly declared static). [Example:

class X {
  void operator delete(void*);
  void operator delete[](void*, std::size_t);
};

class Y {
  void operator delete(void*, std::size_t);
  void operator delete[](void*);
};

end example]

Since member allocation and deallocation functions are static they cannot be virtual. [Note: However, when the cast-expression of a delete-expression refers to an object of class type, because the deallocation function actually called is looked up in the scope of the class that is the dynamic type of the object, if the destructor is virtual, the effect is the same. For example,

struct B {
  virtual ~B();
  void operator delete(void*, std::size_t);
};

struct D : B {
  void operator delete(void*);
};

void f() {
  B* bp = new D;
  delete bp;        // 1: uses D​::​operator delete(void*)
}

Here, storage for the non-array object of class D is deallocated by D​::​operator delete(), due to the virtual destructor. end note] [Note: Virtual destructors have no effect on the deallocation function actually called when the cast-expression of a delete-expression refers to an array of objects of class type. For example,

struct B {
  virtual ~B();
  void operator delete[](void*, std::size_t);
};

struct D : B {
  void operator delete[](void*, std::size_t);
};

void f(int i) {
  D* dp = new D[i];
  delete [] dp;     // uses D​::​operator delete[](void*, std​::​size_­t)
  B* bp = new D[i];
  delete[] bp;      // undefined behavior
}

end note]

Access to the deallocation function is checked statically. Hence, even though a different one might actually be executed, the statically visible deallocation function is required to be accessible. [Example: For the call on line “// 1” above, if B​::​operator delete() had been private, the delete expression would have been ill-formed. end example]

[Note: If a deallocation function has no explicit noexcept-specifier, it has a non-throwing exception specification. end note]

A similar provision is not needed for the array version of operator delete because [expr.delete] requires that in this situation, the static type of the object to be deleted be the same as its dynamic type.