<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=US-ASCII">

<style type="text/css">

body { color: #000000; background-color: #FFFFFF; }
del { text-decoration: line-through; color: #8B0040; }
ins { text-decoration: underline; color: #005100; }

p.example { margin-left: 2em; }
pre.example { margin-left: 2em; }
div.example { margin-left: 2em; }

code.extract { background-color: #F5F6A2; }
pre.extract { margin-left: 2em; background-color: #F5F6A2;
  border: 1px solid #E1E28E; }

p.function { }
.attribute { margin-left: 2em; }
.attribute dt { float: left; font-style: italic;
  padding-right: 1ex; }
.attribute dd { margin-left: 0em; }

blockquote.std { color: #000000; background-color: #F1F1F1;
  border: 1px solid #D1D1D1;
  padding-left: 0.5em; padding-right: 0.5em; }
blockquote.stddel { text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC;
  padding-left: 0.5empadding-right: 0.5em; ; }

blockquote.stdins { text-decoration: underline;
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3; padding: 0.5em; }

table { border: 1px solid black; border-spacing: 0px;
  margin-left: auto; margin-right: auto; }
th { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }
td { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }

</style>

<title>C++ Sized Deallocation</title>
</head>

<body>
<h1>C++ Sized Deallocation</h1>

<p>
ISO/IEC JTC1 SC22 WG21 N3432 = 12-0122 - 2012-09-23
</p>

<p>
Lawrence Crowl, crowl@google.com, Lawrence@Crowl.org
</p>

<p>
<a href="#Problem">Problem</a><br>
<a href="#Solution">Solution</a><br>
<a href="#Implementation">Implementation</a><br>
<a href="#Wording">Wording</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#basic.stc.dynamic">3.7.4 Dynamic storage duration [basic.stc.dynamic]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#basic.stc.dynamic.deallocation">3.7.4.2 Deallocation functions [basic.stc.dynamic.deallocation]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#expr.delete">5.3.5 Delete [expr.delete]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#class.free">12.5 Free store [class.free]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#replacement.functions">17.6.3.6 Replacement functions [replacement.functions]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#support.dynamic">18.6 Dynamic memory management [support.dynamic]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#new.delete.single">18.6.1.1 Single-object forms [new.delete.single]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#new.delete.array">18.6.1.2 Array forms [new.delete.array]</a><br>
</p>


<h2><a name="Problem">Problem</a></h2>

<p>
With C++11,
programmers may define a static member function <code>operator delete</code>
that takes a size parameter indicating the size of the object to be deleted.
The equivalent global <code>operator delete</code> is not available.
This omission has unfortunate performance consequences.
</p>

<p>
Modern memory allocators often allocate in size categories,
and, for space efficiency reasons,
do not store the size of the object near the object.
Deallocation then requires searching for the size category store
that contains the object.
This search can be expensive,
particularly as the search data structures
are often not in memory caches.
</p>


<h2><a name="Solution">Solution</a></h2>

<p>
Permit implementations and programmers
to define sized versions of the global <code>operator delete</code>.
The compiler shall call the sized version
in preference to the unsized version
when the sized version is available.
</p>

<p>
There are two potential problems with this solution.
</p>

<ul>
<li><p>
When deleting an incomplete type, there is no size available.
In this case, the unsized version must be used.
This observation implies that calls to one version
must be effectively equivalent to calls to the other version.
Excepting the specific deallocation function called,
we believe that any programs that would change behavior
already have undefined behavior within the standard.
</p></li>

<li><p>
Existing programs that redefine the global unsized version
do not also define the sized version.
When an implementation introduces a sized version,
the replacement would be incomplete.
In this case, the only viable option
seems to be for the implementation to emit a diagnostic
when the programmer provides an unsized replacement
but does not provide a sized replacement.
The workaround is to define a sized version
that simply calls the unsized version.
</p>
<p>
Note, however, that the converse case is not a problem.
The programmer may define a both an unsized and a sized version
even when the underlying implementation only provides a sized version.
The reason is that the two versions must be functionally equivalent.
</p></li>
</ul>

<p>
As a consequence of the second problem,
we expect vendor implementations to be somewhat conservative
in the introduction of the pre-defined sized versions.
On the other hand, programmers may aggressively define the sized versions.
</p>


<h2><a name="Implementation">Implementation</a></h2>

<p>
Google has implemented much of this proposal within GCC and
TCMalloc <a href="#TCM">[TCM]</a>.
It has obtained significant performance improvements.
</p>


<h2><a name="Wording">Wording</a></h2>

<p>
The proposed wording changes are relative to the Final Committee Draft,
<a href="http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2010/n3092.pdf">N3092</a>.
</p>


<h3><a name="basic.stc.dynamic">3.7.4 Dynamic storage duration [basic.stc.dynamic]</a></h3>

<p>
Edit within paragraph 2 as follows.
</p>

<blockquote class="std">
<p>
....
The following allocation and deallocation functions (18.6)
are implicitly declared in global scope in each translation unit of a program.
</p>
<blockquote><pre><code>
void* operator new(std::size_t) throw(std::bad_alloc);
void* operator new[](std::size_t) throw(std::bad_alloc);
void operator delete(void*) throw();
void operator delete[](void*) throw();
</code></pre></blockquote>
<p><ins>
Furthermore, the implementation may implicitly declare the following functions
in global scope in each translation unit of a program.
</ins></p>
<blockquote><pre><code><ins>
void operator delete(void*, std::size_t) throw();
void operator delete[](void*, std::size_t) throw();
</ins></code></pre></blockquote>
<p><ins>
If the implementation provides these functions,
and if a translation unit provides a definition for an unsized version
but not for a sized version,
the program is ill-formed.
</ins></p>
<p>
These implicit declarations introduce only the function names
<code>operator new</code>, <code>operator new[]</code>,
<code>operator delete</code>, <code>operator delete[]</code>.
....
</p>
</blockquote>


<h3><a name="basic.stc.dynamic.deallocation">3.7.4.2 Deallocation functions [basic.stc.dynamic.deallocation]</a></h3>

<p>
Edit paragraph 2 as follows.
</p>

<blockquote class="std">
<p>
Each deallocation function shall return <code>void</code>
and its first parameter shall be <code>void*</code>.
A deallocation function can have more than one parameter.
<ins>
If there is a declaration of global <code>operator delete</code>
with exactly two parameters,
the second of which has type <code>std::size_t</code> (18.2),
then that function is a usual (non-placement) deallocation function.
Similarly, if there is a declaration of global <code>operator delete[]</code>
with exactly two parameters,
the second of which has type <code>std::size_t</code>,
then this function is a usual deallocation function.
</ins>
If a class <code>T</code> has a member deallocation function
named <code>operator delete</code> with exactly one parameter,
then that function is a usual <del>(non-placement)</del> deallocation function.
If class <code>T</code> does not declare such an <code>operator delete</code>
but does declare a member deallocation function
named <code>operator delete</code> with exactly two parameters,
the second of which has type <code>std::size_t</code><del> (18.2)</del>,
then this function is a usual deallocation function.
Similarly, if a class <code>T</code> has a member deallocation function
named <code>operator delete[]</code> with exactly one parameter,
then that function is a usual (non-placement) deallocation function.
If class <code>T</code> does not declare such an <code>operator delete[]</code>
but does declare a member deallocation function
named <code>operator delete[]</code> with exactly two parameters,
the second of which has type <code>std::size_t</code>,
then this function is a usual deallocation function.
A deallocation function can be an instance of a function template.
Neither the first parameter nor the return type
shall depend on a template parameter.
[<i>Note:</i>
that is, a deallocation function template
shall have a first parameter of type <code>void*</code>
and a return type of <code>void</code> (as specified above).
&mdash;<i>end note</i>]
A deallocation function template
shall have two or more function parameters.
A template instance is never a usual deallocation function,
regardless of its signature.
</p>
</blockquote>


<h3><a name="expr.delete">5.3.5 Delete [expr.delete]</a></h3>

<p>
Paragraph 1 remains unchanged,
though note the restrictions on the delete operand.
</p>

<blockquote class="std">
<p>
....
The operand shall have a pointer to object type,
or a class type having a single non-explicit conversion function (12.3.2)
to a pointer to object type.
The result has type void.
[<i>Footnote:</i>
This implies that an object cannot be deleted
using a pointer of type <code>void*</code>
because void is not an object type.
&mdash;<i>end footnote</i>]
....
</p>
</blockquote>

<p>paragraph 2 remains unchanged,
though note the restriction on inheritance
with respect to the <code>delete</code> operand.</p>

<blockquote class="std">
<p>
....
If it is not a null pointer value,
in the first alternative (<em>delete object</em>),
the value of the operand of <code>delete</code>
shall be a pointer to a non-array object
or a pointer to a subobject (1.8)
representing a base class of such an object (Clause 10).
If not, the behavior is undefined.
In the second alternative (<em>delete array</em>),
the value of the operand of <code>delete</code>
shall be the pointer value which resulted from a previous array new-expression.
[<i>Footnote:</i>
For non-zero-length arrays,
this is the same as a pointer to the first element of the array
created by that new-expression.
Zero-length arrays do not have a first element.
&mdash;<i>end footnote</i>]
If not, the behavior is undefined.
[<i>Note:</i>
this means that the syntax of the <var>delete-expression</var>
must match the type of the object allocated by <code>new</code>,
not the syntax of the <var>new-expression</var>.
&mdash;<i>end note</i>]
....
</p>
</blockquote>


<p>
Paragraph 3 remains unchanged,
though note the further restriction on inheritance.
</p>

<blockquote class="std">
<p>
In the first alternative (<em>delete object</em>),
if the static type of the object to be deleted
is different from its dynamic type,
the static type shall be a base class of the dynamic type
of the object to be deleted
and the static type shall have a virtual destructor
or the behavior is undefined.
In the second alternative (<em>delete array</em>)
if the dynamic type of the object to be deleted differs from its static type,
the behavior is undefined.
</p>
</blockquote>

<p>
Paragraph 5 remains unchanged.
</p>

<blockquote class="std">
<p>
If the object being deleted has incomplete class type
at the point of deletion
and the complete class has a non-trivial destructor or a deallocation function,
the behavior is undefined.
</p>
</blockquote>

<p>
Edit paragraph 9 as follows.
</p>

<blockquote class="std">
<p>
<del>
When the keyword <code>delete</code> in a <var>delete-expression</var>
is preceded by the unary <code>::</code> operator,
the global deallocation function is used to deallocate the storage.
</del>
<ins>
If a <var>delete-expression</var> begins with a unary <code>::</code> operator,
the deallocation function's name is looked up in global scope.
Otherwise,
the lookup considers class-specific deallocations (12.5 [class.free]).
If no class-specific deallocation is found,
the deallocation function's name is looked up in global scope.
If the lookup selects a placement deallocation function,
the program is ill-formed.
</ins>
</p>
</blockquote>

<p>
Add a new paragraph as follows.
</p>

<blockquote class="stdins">
<p>
If deallocation function lookup finds
both a usual deallocation function with one parameter
and a usual deallocation function with two parameters,
and then if the object being deleted has a complete class type,
the selected deallocation function shall be the one with two parameters.
Otherwise, the selected deallocation function
shall be the function with one parameter.
</p>
</blockquote>

<p>
Add a new paragraph as follows.
This paragraph is identical to the existing 12.5/5.
</p>

<blockquote class="stdins">
<p>
When a <var>delete-expression</var> is executed,
the selected deallocation function shall be called
with the address of the block of storage to be reclaimed as its first argument
and (if the two-parameter style is used)
the size of the block as its second argument.
[<i>Footnote:</i>
If the static type of the object to be deleted
is different from the dynamic type and the destructor is not virtual
the size might be incorrect,
but that case is already undefined, as stated above.
&mdash;<i>end footnote</i>]
</p>
</blockquote>


<h3><a name="class.free">12.5 Free store [class.free]</a></h3>

<p>
Edit paragraph 4 as follows.
</p>

<blockquote class="std">
<p>
<ins>
Class-specific deallocation function lookup
is a part of general deallocation function lookup (5.3.5 [expr.delete])
and occurs as follows.
</ins>
<del>
If a <var>delete-expression</var> begins with a unary <code>::</code> operator,
the deallocation function's name is looked up in global scope.
Otherwise, if</del>
<ins>If</ins> the <var>delete-expression</var>
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 (12.4).
[<i>Footnote:</i>
A similar provision is not needed
for the array version of <code>operator delete</code>
because 5.3.5 requires that in this situation,
the static type of the object to be deleted be the same as its dynamic type.
&mdash;<i>end footnote</i>]
Otherwise, if the <var>delete-expression</var>
is used to deallocate an object of class <code>T</code> 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 <code>T</code>.
If this lookup fails to find the name,
<del>
the name is looked up in the global scope.
</del>
<ins>
the class-specific deallocation function lookup has failed
and general deallocation function lookup (5.3.5 [expr.delete]) continues.
</ins>
If the result of the lookup is ambiguous or inaccessible,
or if the lookup selects a placement deallocation function,
the program is ill-formed.
</p>
</blockquote>

<p>
Remove paragraph 5 as follows.
This paragraph moves to 5.3.5/9++.
</p>

<blockquote class="stddel">
<p>
<del>
When a <var>delete-expression</var> is executed,
the selected deallocation function shall be called
with the address of the block of storage to be reclaimed as its first argument
and (if the two-parameter style is used)
the size of the block as its second argument.
[<i>Footnote:</i>
If the static type of the object to be deleted
is different from the dynamic type and the destructor is not virtual
the size might be incorrect,
but that case is already undefined; see 5.3.5.
&mdash;<i>end footnote</i>]
</del>
</p>
</blockquote>


<h3><a name="replacement.functions">17.6.3.6 Replacement functions [replacement.functions]</a></h3>

<p>
Edit paragraph 2 as follows.
</p>

<blockquote class="std">
<p>
A C++ program may provide the definition
for any of eight dynamic memory allocation function signatures
declared in header <code>&lt;new&gt;</code>
(3.7.4, <del>Clause 18</del> <ins>18.4 [support.dynamic]</ins>):
</p>
<ul>
<li><code>operator new(std::size_t)</code></li>
<li><code>operator new(std::size_t, const std::nothrow_t&amp;)</code></li>
<li><code>operator new[](std::size_t)</code></li>
<li><code>operator new[](std::size_t, const std::nothrow_t&amp;)</code></li>
<li><code>operator delete(void*)</code></li>
<li><code>operator delete(void*, const std::nothrow_t&amp;)</code></li>
<li><code>operator delete[](void*)</code></li>
<li><code>operator delete[](void*, const std::nothrow_t&amp;)</code></li>
</ul>
<p>
Furthermore, a C++ program may provide the definition
for any of the four dynamic memory deallocation function signatures
that implementations may choose to declare in header <code>&lt;new&gt;</code>:
</p>
<ul>
<li><ins><code>operator delete(void*, std::size_t)</code></ins></li>
<li><ins><code>operator delete(void*, std::size_t, const std::nothrow_t&amp;)</code></ins></li>
<li><ins><code>operator delete[](void*, std::size_t)</code></ins></li>
<li><ins><code>operator delete[](void*, std::size_t, const std::nothrow_t&amp;)</code></ins></li>
</ul>
</blockquote>


<h3><a name="support.dynamic">18.6 Dynamic memory management [support.dynamic]</a></h3>

<p>
At the end of the synopsis add the following.
</p>

<blockquote class="stdins">
<p>
The implementation may, but need not,
provide the following additional group of functions.
</p>
<blockquote>
<pre><code>
operator delete(void* ptr, std::size_t size) throw();
operator delete(void* ptr, std::size_t size, const std::nothrow_t&amp;) throw();
operator delete[](void* ptr, std::size_t size) throw();
operator delete[](void* ptr, std::size_t size, const std::nothrow_t&amp;) throw();
</code></pre>
</blockquote>
</blockquote>


<h3><a name="new.delete.single">18.6.1.1 Single-object forms [new.delete.single]</a></h3>

<p>
At the end of the section, add a new paragraph as follows.
</p>

<blockquote class="stdins">
<p>
<code>operator delete(void* ptr, std::size_t size) throw();<br>
operator delete(void* ptr, std::size_t size, const std::nothrow_t&amp;) throw();</code>
</p>
</blockquote>

<p>
Add a new paragraph as follows.
</p>

<blockquote class="stdins">
<p>
These functions behave as their corresponding version
without the <code>std::size_t size</code> parameter,
with the additional constraints:
</p>
<blockquote>
<p>
<i>Requires:</i>
<code>size</code> shall equal that used to allocate <code>ptr</code>.
</p><p>
<i>Required behavior:</i>
Calls to the sized and unsized versions shall be interchangable.
</p>
</blockquote>
</blockquote>


<h3><a name="new.delete.array">18.6.1.2 Array forms [new.delete.array]</a></h3>

<p>
At the end of the section, add a new paragraph as follows.
</p>

<blockquote class="stdins">
<p>
<code>operator delete[](void* ptr, std::size_t size) throw();<br>
operator delete[](void* ptr, std::size_t size, const std::nothrow_t&amp;) throw();</code>
</p>
</blockquote>

<p>
Add a new paragraph as follows.
</p>

<blockquote class="stdins">
<p>
These functions behave as their corresponding version
without the <code>std::size_t size</code> parameter,
with the additional constraints:
</p>
<blockquote><p>
<i>Requires:</i>
<code>size</code> shall equal that used to allocate <code>ptr</code>.
</p><p>
<i>Required behavior:</i>
Calls to the sized and unsized versions shall be interchangable.
</p>
</blockquote>
</blockquote>


<h2><a name="References">References</a></h2>

<dl>

<dt><a name="TCM">[TCM]</a></dt>
<dd>
<cite>TCMalloc : Thread-Caching Malloc</cite>,
<a href="http://goog-perftools.sourceforge.net/doc/tcmalloc.html">
http://goog-perftools.sourceforge.net/doc/tcmalloc.html</a>.
</dd>

</dl>

</body>
</html>
