
<html>
<head>
<meta http-equiv="Content-Type" content="text/html">
<style type="text/css">
  ins { text-decoration:none; background-color:#A0FFA0 }
  del { text-decoration:line-through; background-color:#FFA0A0 }
  xins { font-weight: inherit; color: #2020ff }

  tab { padding-left: 4em; }
  tab2 { padding-left: 2em; }
   ul.dashed { list-style: none;  margin-left: 0; padding-left: 1.5em; text-indent: -1.5em; }
   ul.dashed li:before { content: "\2014\a0" }
</style>
<title>More constexpr containers</title>
</head>


<body>
<p align="right"><b>P0784R7, 2019-07-19</b>
<br>LWG, CWG
</p>

<p align="right">
<br>Peter Dimov (pdimov@pdimov.com)
<br>Louis Dionne (ldionne.2@gmail.com)  
<br>Nina Ranns (dinka.ranns@gmail.com)
<br>Richard Smith (richard@metafoo.co.uk)  
<br>Daveed Vandevoorde (daveed@edg.com)
</p>

<h1>More constexpr containers</h1>

<p>
<br>R0: Original proposal, presented in Albuquerque 2017.
<br><tab2>EWG approved the direction: SF: 11 | F: 12 | N: 2 | A: 0 | SA: 0
<br>R1: Provided initial wording.
<br><tab2>EWG approved the proposal as presented: SF: 23 | F: 11 | N: 0 | A: 0 | SA: 0
<br>R2: Implemented core review comments.
<br>R3: Extends proposal for non-transient allocations.
<br>R4: Tighten wording for "construct" and "destruct".
<br>R5: (submitted for Kona 2019):
	<br><tab2>Introduce <tt>mark_immutable_if_constexpr</tt> to address some recently found issue.
	<br><tab2>Remove prohibition on virtual destructors (i.e., merge in P1077) and pseudo-destructor calls (an earlier oversight).
	<br><tab2>Replace <tt>construct</tt>/<tt>destruct</tt> interception by <tt>construct_at</tt>/<tt>destroy_at</tt> (and friends_.
	<br><tab2>Integrate San Diego CWG review notes.
	<br><tab2> Also rebase on N4778. 
<br>R6: (submitted for Cologne 2019):
	<br><tab2>Remove non-transient allocation handling because EWG did not
	    like <tt>mark_immutable_if_constexpr</tt> and the problem of the R4
	    status quo is potentially difficult to evolve.
	<br><tab2>Add constexpr to the definition of <tt>voidify</tt> and amend the call to it in [specialized.construct].
<br>R7:
<br><tab2>Move constraint from contruct_at declaration into description.
<br><tab2>Fix missing template parameters in various places.
<br><tab2>Implemented core review comments.
<br><tab2>Implemented library review comments.
</p>

<h3>Introduction and motivation</h3>

<p>Variable size container types, like <code>std::vector</code> or
<code>std::unordered_map</code>, are generally useful for runtime programming,
and therefore also potentially useful in constexpr computations. This has been
made clear by some recent experiments such as the
<a href="https://youtu.be/HMB9oXFobJc">Constexpr ALL the things!</a>
presentation (and its companion paper
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0810r0.pdf">P0810R0</a>
published in the pre-Albuquerque mailing) by Ben Deane and Jason Turner,
in which they build a compile-time JSON parser and JSON value representation
using <code>constexpr</code>. Amongst other things, the lack of variable size
containers forces them to use primitive fixed-size data structures in the
implementation, and to parse the input JSON string twice; once to determine the
size of the data structures, and once to parse the JSON into those
structures.</p>

<p>We also expect variable size containers to be a necessity in the reflection
and metaprogramming APIs that will emerge from the work in SG-7, which decided
that the preferred direction for a standard solution would involve
constexpr-like computation. For example, querying the template arguments of a
class type might look something like:</p>

<pre><code class="language-c++">std::vector&lt;std::metainfo&gt; args = std::meta::get_template_args(reflexpr(T));
</code></pre>

<h3>Non-transient allocation</h3>
<p>
Earlier versions of this paper (<a href="http://wg21.link/P0784R1">P0784r1</a> and
<a href="http://wg21.link/P0784R2">P0784r2</a>) proposed the changes needed for
constexpr destructors and for so-called "transient constexpr allocations".
Transient constexpr allocations are dynamic memory allocations occurring
during a constexpr evaluation that are deallocated before that
evaluation completes.
</p>

<p>What about dynamically allocated constexpr storage that hasn't been deallocated by the time evaluation
completes?  There are really compelling use
cases where this might be desirable. E.g., this could be the basis for a more
flexible kind of "string literal" class.  We therefore proposed that
a non-transient constexpr allocation be a valid result for a constexpr
variable initializer if:</p>

<ul>
<li>the result of evaluating the initializer is an object with a nontrivial
	constexpr destructor, and</li>
<li>evaluating that destructor would be a valid core constant expression and
	would deallocate all the <i>non-transient</i> allocations produced by
	the evaluation of <i>expr</i>.</li>
</ul>
<p>
Furthermore, we specify that an attempt to deallocate a non-transiently allocated
object by any other means results in undefined behavior.  (Note that this is unlikely
because the object pointing to the allocated storage is immutable.)</p>
<p>
A question that arises in this context is whether the non-transient allocation
is mutable between the completion of the initialization and the	evaluation of 
the destructor.  If the allocation is mutable, reading from that allocation
during the destructor evaluation is meaningless and should thus not be accepted
as part of a core constant expression evaluation.  However, there are cases
where having a mutable allocation is desirable.  To permit both cases, we
proposed a library function "std::mark_immutable_if_constexpr" to designate
that a constexpr allocation is immutable in the context.
</p>	

<p>For example:</p>

<pre><code class="language-c++">#include &lt;memory&gt;
#include &lt;new&gt;
using namespace std;
template&lt;typename T&gt; struct S: allocator&lt;T&gt; {
  T *ps;
  int sz;
  template&lt;int N&gt; constexpr S(T (&amp;p)[N])
                          : sz{N}
                          , ps{this-&gt;allocate(N)} {
    for (int k = 0; k&lt;N; ++k) {
      new(this-&gt;ps+k) T{p[k]};
    }
    std::mark_immutable_if_constexpr(this-&gt;ps);
  }
  constexpr ~S() {
    for (int k = 0; k&lt;this-&gt;sz; ++k) {
      (this-&gt;ps+k)-&gt;T::~T();
    }
    this-&gt;deallocate(this-&gt;ps, this-&gt;sz);
  }
};

constexpr S&lt;char&gt; str("Hello!");
  // str ends up pointing to a static array
  // containing the string "Hello!".
</code></pre>

<p>The constructor constexpr evaluation in this example is successful,
producing an <code>S</code> object that points to a non-transient constexpr
allocation.  The constexpr evaluation of the destructor would also be
successful and would deallocate the non-transient allocation. The non-transient
allocation is therefore promoted to static storage.</p>
	
<p>In Kona 2019, this approach was considered too brittle, and as a
	result non-transient allocation was removed from the feature
	set. 
	
<br/><tab2>Proposal P0784R5 as presented, for C++20.
<br/><tab2>SF: 3 | F: 4 | N: 8 | A: 5 | SA: 8
<br/>
<br/>Adopt P0784R5 without non-transient allocation into C++20.
<br/><tab2>SF: 10 | F: 13 | N: 2 | A: 0 | SA: 0
</p>

<h3>Virtual constexpr destructors</h3>
<p>In Rapperswil, Peter Dimov proposed (P1077) to let literal types have
virtual destructors.  That proposal passed easily:
	<br/><tab2>Allowing literal types to have virtual destructors:
	<br/><tab2>SF: 11 | F: 12 | N: 2 | A: 0 | SA: 0
</p>
<p>The notion of <i>quasi-trivial destructor</i> in P1077, however,
is subsumed by this paper's notion of <i>constexpr destructor</i>.
It was therefore decided to merge P1077 into this paper (with P1064,
which permits virtual constexpr functions already approved by WG21
and edited into the current draft working paper).</p>
	
<h3>Implementation experience</h3>

<p>The proposal in this paper (which now excludes non-transient allocations)
has been implemented in the EDG compiler.  However, no public deployment has
been made of that implementation.  Based on preliminary discussion with
implementers working on Clang and MSVC, no blockers that would make this
feature unimplementable or prohibitively expensive to implement
have been identified.</p>

<h3>Acknowledgments</h3>
<p>Special thanks to Billy O'Neal for helping formulate the library wording changes.</p>


<h2>Wording changes</h2>

<p><i>
	<b>Note 1: </b>These are cummulative changes: They include the changes
	EWG approved for P0784r1, and build on top of that.
</i></p>

<p><i>
	<b>Note 2: </b>The following changes enable "constexpr destructors".
	See further down for allocation-related changes.
</i></p>

<p>Change in [basic.types] bullet 10.5.1:</p>
<blockquote style="padding-left: 30px;">&mdash; it has a 
<del>trivial</del><ins>constexpr</ins> 
destructor <ins>([dcl.constexpr])</ins>,
</blockquote>
 

<p>Change in [expr.const] paragraph 4:</p>

<blockquote>An expression <code>e</code> is a <i>core constant expression</i>
unless the evaluation of <code>e</code>, following the rules of the abstract
machine (6.8.1), would evaluate one of the following expressions:</blockquote>

<blockquote style="padding-left: 30px;"> &mdash; <code>this</code> (7.5.2),
except in a constexpr function<del> or a constexpr
constructor</del> <ins>([dcl.constexpr])</ins> that is being
evaluated as part of e;
</blockquote>

<blockquote style="padding-left: 30px;"> &mdash; an invocation of a
<ins>non-constexpr</ins> function
<del>other than a constexpr constructor for a
literal class, a constexpr function, or an implicit
invocation of a trivial destructor (10.3.7)</del> [ <i>Note:</i>
Overload resolution (11.3) is applied as usual &mdash; <i>end note</i> ];
</blockquote>

<blockquote style="padding-left: 30px;"> &mdash; an invocation of an
undefined constexpr function<del> or an undefined
constexpr constructor</del>;
</blockquote>

<blockquote style="padding-left: 30px;"> &mdash; an invocation of an
instantiated constexpr function<del> or a constexpr
constructor</del> that fails to satisfy the
requirements for a constexpr function <del>or a constexpr
constructor (10.1.5)</del>;
</blockquote>


<blockquote style="padding-left: 30px;"> &mdash;  an <i>id-expression</i>
that refers to a variable or data member of reference type unless the
reference has a preceding initialization and either
</blockquote>

<blockquote style="padding-left: 60px;"> &mdash; it is initialized with a
constant expression or
</blockquote>

<blockquote style="padding-left: 60px;"> &mdash; its lifetime began within
the evaluation of <code>e</code>;
</blockquote>

<blockquote style="padding-left: 30px;"> &mdash; modification of an object
(7.6.18, 7.6.1.6, 7.6.2.2) unless it is applied to a non-volatile lvalue of literal
type that refers to non-volatile object whose lifetime began within the
evaluation of <code>e</code>
</blockquote>


<p>Add new paragraph after [expr.const] paragraph 4:</p>


<blockquote><ins>An object <i>a</i> is said to have <i>constant destruction</i> if: </ins></blockquote>
<blockquote style="padding-left: 30px;"><ins> &mdash; it is not of class type nor (possibly multi-dimensional) array thereof, or</ins></blockquote>
<blockquote style="padding-left: 30px;"><ins> &mdash; it is of class type or (possibly multi-dimensional) array thereof,
    that class type has a constexpr destructor, and for a hypothetical expression
<code>e</code> whose only effect is to destroy <i>a</i>,
<code>e</code> would be a core constant expression
if the lifetime of <i>a</i>
and its non-mutable subobjects (but not its mutable subobjects)
were considered to start within <code>e</code>.
</blockquote>



<p>Change in [dcl.constexpr] paragraph 2:</p>

<blockquote>A <code>constexpr</code> or <code>consteval</code> specifier
used in the declaration of a function <del>that is not a constructor</del>
declares that function to be a <i>constexpr function</i>. <del>Similarly,
a  <code>constexpr</code> or <code>consteval</code> specifier used in a
constructor declaration declares that constructor to be a
<i>constexpr constructor</i>.</del>
</blockquote>

<p>Change in [dcl.constexpr] paragraph 3 bullet 1:</p>

<blockquote>
  its return type <ins>(if any)</ins> shall be a literal type;
</blockquote>

<p>Add a bullet to [dcl.constexpr] paragraph 3:</p>

<blockquote>
  <ins>if the function is a constructor or destructor,
    its class shall not have any virtual base classes;</ins>
</blockquote>

<p>Change in [dcl.constexpr] paragraph 4:</p>

<blockquote>
The definition of a constexpr constructor
<ins>
whose <i>function-body</i> is not <tt>= delete</tt>
</ins>
shall
<ins>additionally</ins> satisfy the following requirements:
<ul>
  <li><del>the class shall not have any virtual base classes;</del>
  <li><del>each of the parameter types shall be a literal type;</del>
</ul>
<del>In addition, either its function-body shall be = delete,
  or it shall satisfy the following requirements:</del>
<ul>
  <li><del>either its function-body shall be = default, or the compound-statement of its function-body shall satisfy
    the requirements for a function-body of a constexpr function;</del>
  <li>every non-variant non-static data member and base class subobject shall be initialized (10.9.2);
  <li>if the class is a union having variant members (10.4), exactly one of them shall be initialized;
  <li>if the class is a union-like class, but is not a union, for each of its anonymous union members having variant members, exactly one of them shall be initialized;
  <li>for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
  <li>for a delegating constructor, the target constructor shall be a constexpr constructor.
</ul>
[ Example: &hellip; ]
</blockquote>

<p>Insert new paragraph after [dcl.constexpr] paragraph 4:</p>

<blockquote><ins>The definition of a constexpr destructor
whose <i>function-body</i> is not <tt>= delete</tt>
shall additionally satisfy the following requirement:</ins>
</blockquote>
<blockquote style="padding-left: 30px;"><ins>&mdash;
    for every subobject of class type
    or (possibly multi-dimensional) array thereof,
    that class type shall have a constexpr destructor.</ins>
</blockquote>

<p>Change in [dcl.constexpr] paragraph 6:</p>

<blockquote>If the instantiated template specialization of a constexpr
function template or member function of a class template would fail to
satisfy the requirements for a constexpr function<del> or
a constexpr constructor</del>, that
specialization is still a constexpr function<del> or
a constexpr constructor</del>, even though a
call to such a function cannot appear in a constant expression. If no
specialization of the template would satisfy the requirements for a constexpr
function<del> or a constexpr constructor</del>,
when considered as a non-template function<del>
or constructor</del>, the template is ill-formed, no diagnostic required.
</blockquote>

<p>Change in [dcl.constexpr] paragraph 8:</p>

<blockquote>The constexpr specifier has no effect on the type of a
constexpr function<del> or a constexpr constructor</del>.
</blockquote>

<p>Change in [dcl.constexpr] paragraph 9:</p>

<blockquote>In any <code>constexpr</code> variable declaration, the
full-expression of the initialization shall be a constant expression (7.7).
<ins>A <code>constexpr</code> variable shall have constant
destruction.</ins>
</blockquote>

<p>Change in [class.dtor]  paragraph 1:</p>

<blockquote>Each decl-specifier of the decl-specifier-seq of a destructor
declaration (if any) shall be <code>friend</code>, <code>inline</code>,
<del> or</del> <code>virtual</code><ins>, <code>constexpr</code>,
or <code>consteval</code></ins>.
</blockquote>

<p>Add after [class.dtor] paragraph 9:</p>

<blockquote>
<ins>The defaulted destructor is a constexpr destructor
if it satisfies the requirements for a constexpr destructor ([dcl.constexpr]).
[  <i>Note:</i> In particular, a trivial destructor is a constexpr
   destructor. &mdash; <i>end note</i> ]
</ins>
</blockquote>



<p><i>
	<b>Note 3: </b>The following changes enable some
	"constexpr new-expressions".
</i></p>


<p>Modify [expr.new] paragraph 12 (the deleted part will be re-inserted below)</p>

<blockquote>An implementation is allowed to omit a call to a replaceable global
allocation function (16.6.2.1, 16.6.2.2).  When it does so, the storage
is instead provided by the implementation or provided by extending the
allocation of another <i>new-expression.</i>
</blockquote>

<blockquote><del>The implementation may extend the allocation of a
new-expression <code>e1</code> to provide storage for a
<i>new-expression</i> <code>e2</code> if the following would
be true were the allocation not extended:</del></blockquote>	

<blockquote style="padding-left: 30px;"><del>&mdash; the evaluation of
<code>e1</code> is sequenced before the evaluation of <code>e2</code>,
and</del>
</blockquote>

<blockquote style="padding-left: 30px;"><del>&mdash; <code>e2</code> is
evaluated whenever <code>e1</code> obtains storage, and</del>
</blockquote>

<blockquote style="padding-left: 30px;"><del>&mdash; both <code>e1</code>
and <code>e2</code> invoke the same replaceable global allocation function,
and</del>
</blockquote>

<blockquote style="padding-left: 30px;"><del>&mdash; if the allocation
function invoked by <code>e1</code> and <code>e2</code> is throwing,
any exceptions thrown in the evaluation of either <code>e1</code> or
<code>e2</code> would be first caught in the same handler, and</del>
</blockquote>

<blockquote style="padding-left: 30px;"><del>&mdash; the pointer values
produced by <code>e1</code> and <code>e2</code> are operands to
evaluated delete-expressions, and</del>
</blockquote>

<blockquote style="padding-left: 30px;"><del>&mdash; the evaluation
of <code>e2</code> is sequenced before the evaluation of the
delete-expression whose operand is the pointer value produced by
<code>e1</code>.</del></blockquote>

<blockquote><del>[<i>Example:</i></del></blockquote>

<blockquote><del>...</del></blockquote>

<blockquote><del>&mdash; <i>end example</i> ]</del></blockquote>

<p>Add new paragraph after [expr.new] paragraph 12</p>

<blockquote><ins>During an evaluation of a constant expression, a call to
an allocation function is always omitted.  [ Note: Only
<i>new-expression</i>s that would otherwise result in a call to a
replaceable global allocation function can be evaluated in
constant expressions (see [expr.const]). &mdash; <i>end note</i> ]</ins>
</blockquote>


<p>Add new paragraph after [expr.new] the previously-insert paragraph
(this was originally part of paragraph 12)</p>

<blockquote><ins>The implementation may extend the allocation of a
<i>new-expression</i> <code>e1</code> to provide
storage for a <i>new-expression.</i> <code>e2</code> if the following
would be true were the allocation not extended:</ins>
</blockquote>	

<blockquote style="padding-left: 30px;"><ins>&mdash; the evaluation
of <code>e1</code> is sequenced before the evaluation of
<code>e2</code>, and</ins>
</blockquote>

<blockquote style="padding-left: 30px;"><ins>&mdash; <code>e2</code>
is evaluated whenever <code>e1</code> obtains storage, and</ins>
</blockquote>

<blockquote style="padding-left: 30px;"><ins>&mdash; both
<code>e1</code> and <code>e2</code> invoke the same replaceable
global allocation function, and</ins>
</blockquote>

<blockquote style="padding-left: 30px;"><ins>&mdash; if the allocation
function invoked by <code>e1</code> and <code>e2</code> is throwing,
any exceptions thrown in the evaluation of either <code>e1</code> or
<code>e2</code> would be first caught in the same handler, and</ins>
</blockquote>

<blockquote style="padding-left: 30px;"><ins>&mdash; the pointer
values produced by <code>e1</code> and <code>e2</code> are operands
to evaluated <i>delete-expression</i>s, and</ins>
</blockquote>

<blockquote style="padding-left: 30px;"><ins>&mdash; the evaluation
of <code>e2</code> is sequenced before the evaluation of the
<i>delete-expression</i> whose operand is the pointer value produced by
<code>e1</code>.</ins>
</blockquote>

<blockquote><ins>[<i>Example:</i></ins></blockquote>

<blockquote><ins>...</ins></blockquote>

<blockquote><ins>&mdash; <i>end example</i> ]</ins></blockquote>


<p>Change in [expr.const] paragraph 4:</p>

<blockquote><del>&mdash; a pseudo-destructor call (7.6.1.10);</del>
</blockquote>
<blockquote>...</blockquote>	
<blockquote><del>&mdash; a <i>new-expression (7.6.2.4)</i>;</del>
</blockquote>  

 
<blockquote>&mdash; <ins>a <i>new-expression</i> (7.6.2.4),
unless the selected allocation function is a replaceable global allocation
function (16.6.2.1, 16.6.2.2) and the allocated storage is
	deallocated within the evaluation of <tt>e</tt>;</ins>
</blockquote>    

<blockquote>&mdash; a <i>delete-expression (7.6.2.5)</i><ins>
	unless it deallocates a region of storage allocated
	within the evaluation of <tt>e</tt></ins>;
</blockquote>   

<blockquote>&mdash; <ins>a call to an instance of
	<tt>std::allocator&lt;T&gt;::allocate</tt> (_allocator.members_),
	unless the allocated storage is
	deallocated within the evaluation of <tt>e</tt>;</ins>
</blockquote>    

<blockquote>&mdash; <ins>a call to an instance of
	<tt>std::allocator&lt;T&gt;::deallocate</tt> (_allocator.members_),
	unless it deallocates a region of storage allocated
	within the evaluation of <tt>e</tt>;</ins>
</blockquote>  

<p><i>
	<b>Note 4: </b>The following changes enable the use of the
	default allocator in constant expressions.
</i></p>

<p>Add a new paragraph after [expr.const] paragraph 4:</p>

<blockquote><ins>
For the purposes of determining whether an expression is a core constant
expression, the evaluation of a call to a member function of
<tt>std::allocator&lt;T&gt;</tt> as defined in _allocator.members_,
where <tt>T</tt> is a literal type,
does not disqualify the expression from being a core constant expression,
even if the actual evaluation of such a call would
otherwise fail the requirements for a core constant expression.


Similarly, the evaluation of a call to <tt>std::destroy_at</tt>,
<tt>std::ranges::destroy_at</tt>, <tt>std::construct_at</tt>,
or <tt>std::ranges::construct_at</tt> is a valid core constant
expression unless
	
<!-- FIXME: Example! -->

<blockquote>
	<ul class="dashed">
	<li><ins>for a call to <tt>std::construct_at</tt> or
	<tt>std::ranges::construct_at</tt>, the
	first argument,	of type T*, does not point to storage
	allocated with <tt>std::allocator&lt;T&gt;</tt> or
	the evaluation of the underlying constructor call is
	not a core constant expression, or</ins></li>

	<li><ins>for a call to <tt>std::destroy_at</tt> or
	<tt>std::ranges::destroy_at</tt>, the
	first argument, of type T*, does not point to storage
	allocated with <tt>std::allocator&lt;T&gt;</tt> or
	the evaluation of the underlying destructor call is
	not a core constant expression.</ins></li>
</blockquote>
</ins></blockquote>
	
<p>Modify [specialized.algorithms] paragraph 6 to add the <tt>constexpr</tt> specifier to the declaration of <tt>voidify</tt>:<br/>
<blockquote>
<tt><tab>template&lt;class T&gt;</tt>
<br/><tt><tab><tab><ins>constexpr </ins>void* <i>voidify</i>(T& ptr) noexcept {</tt>
<br/><tt><tab><tab><tab>return const_cast&lt;void*&gt;(static_cast&lt;const volatile void*&gt;(addressof(ptr)));</tt>
<br/><tt><tab><tab>}</tt>
</blockquote>
</p>

<p>In [memory.syn] paragraph 1 and [specialized.destroy] paragraph 1
add the <tt>constexpr</tt> specifier to the all the declarations of
<tt>destroy_at</tt>, <tt>destroy</tt>, and <tt>destroy_n</tt> (both
in namespace <tt>std</tt> and in namespace <tt>std::ranges</tt>).</p>

<blockquote>
<tt><tab>template&lt;class T&gt;</tt>
<br/><tt><tab><tab><ins>constexpr </ins>void destroy_at(T* location);</tt>
<br/><tt><tab>template&lt;class ForwardIterator&gt;</tt>
<br/><tt><tab><tab><ins>constexpr </ins> void destroy(ForwardIterator first, ForwardIterator last);</tt>
<br/><tt><tab>template&lt;class ExecutionPolicy, class ForwardIterator&gt;</tt>
<br/><tt><tab><tab><ins>constexpr </ins> void destroy(ExecutionPolicy&amp;&amp; exec, // </tt>see 25.3.5
<br/><tt><tab><tab>               ForwardIterator first, ForwardIterator last);</tt>
<br/><tt><tab>template&lt;class T, class Size&gt;</tt>
<br/><tt><tab><tab><ins>constexpr </ins> ForwardIterator destroy_n(ForwardIterator first, Size n);</tt>
<br/><tt><tab>template&lt;class ExecutionPolicy, class ForwardIterator, class Size&gt;</tt>
<br/><tt><tab><tab><ins>constexpr </ins>ForwardIterator destroy_n(ExecutionPolicy&amp;&amp; exec, ForwardIterator first, Size n); // </tt>see 25.3.5
<br/>
<br/><tab><tt>namespace ranges {</tt>
<br/><tt><tab><tab>template&lt;Destructible T&gt;</tt>
<br/><tt><tab><tab><tab><ins>constexpr </ins>void destroy_at(T* location) noexcept;</tt>
<br/><tt><tab><tab>template&lt;<i>no-throw-input-iterator</i> I,
	                       <i>no-throw-sentinel&lt;I&gt; S</i>&gt;
        requires Destructible&lt;iter_value_t&lt;I&gt;&gt;</tt>

<br/><tt><tab><tab><tab><ins>constexpr </ins>I destroy(I first, S last) noexcept;</tt>
<br/><tt><tab><tab>template&lt;no-throw-input-range R&gt;
	requires Destructible&lt;iter_value_t&lt;iterator_t&lt;R&gt;&gt;<ins>&gt;</ins></tt>
	<br/><tt><tab><tab><tab><ins>constexpr </ins>safe_iterator_t&lt;R&gt; destroy(R&amp;&amp; r) noexcept;</tt>

<br/><tt><tab><tab>template&lt;no-throw-input-iterator I&gt;
	requires Destructible&lt;iter_value_t&lt;I&gt;&gt;</tt>
	  <br/><tt><tab><tab><tab><ins>constexpr </ins>I destroy_n(I first, iter_difference_t&lt;I&gt; n) noexcept;</tt>
	  <br/><tt><tab>}</tt>
<br/>
</blockquote>

<p>In [memory.syn] paragraph 1, after the declarations of <tt>destroy</tt>
add declarations for <tt>construct_at</tt> as follows</tt>
	
<blockquote><ins>
<tt>template&lt;class T, class... Args&gt;<br/>
<tab>constexpr T* construct_at(T* location, Args&amp;&amp;... args);</tt>
<br/><br/>
<tt>namespace ranges {<br/>
<tab>template&lt;class T, class... Args&gt;<br/>
<tab><tab><tab>constexpr T* construct_at(T* location, Args&amp;&amp;... args);</tt>
</ins></blockquote>

<p> and following [specialized.destroy] add a new subsection
	[specialized.construct] as follows:</p>

<blockquote><ins>
<tt>template&lt;class T, class... Args&gt;<br/>
<tab>constexpr T* construct_at(T* location, Args&amp;&amp;... args);</tt>
<br/><br/>
<tt>namespace ranges {<br/>
<tab>template&lt;class T, class... Args&gt;<br/>
<tab><tab><tab>constexpr T* construct_at(T* location, Args&amp;&amp;... args);<br/>
}</tt>
<br/><br/>
<i>Constraints:</i> The expression <tt>::new (declval&lt;void*&gt;()) T(declval&lt;Args&gt;()...)</tt> is well-formed when treated as an unevaluated operand</tt>.</br>
<i>Effects:</i> Equivalent to:<br>
<tt><tab><tab><tab>return ::new (<i>voidify</i>(*location)) T(std::forward&lt;Args&gt;(args)...);</tt>
</ins></blockquote>

<p>Modify [allocator.traits.members] paragraph 5 (about the <tt>construct</tt>
member) as follows:<br>
<blockquote>
	<i>Effects</i>: Calls <tt>a.construct(p, std::forward&lt;Args&gt;(args)...)</tt>
	if that call is well-formed; otherwise, invokes
	<del><tt>::new (static_cast&lt;void*&gt;(p)) T(std::forward&lt;Args&gt;(args)...)</tt></del>
	<ins><tt>construct_at(p, std::forward&lt;Args&gt;(args)...)</tt></del>.
</blockquote>
</p>	

<p>Modify [allocator.traits.members] paragraph 6 (about the <tt>destroy</tt>
member) as follows:<br>
<blockquote>
	<i>Effects</i>: Calls <tt>a.destroy(p)</tt>
	if that call is well-formed; otherwise, invokes
	<del><tt>p->~T()</tt></del>
	<ins><tt>destroy_at(p)</tt></del>.
</blockquote>
</p>

<p>In [allocator.traits]/1 add <tt>constexpr</tt> to all the member functions.<br/>
<blockquote>
<tt> <tab>[[nodiscard]] static <ins>constexpr </ins>pointer allocate(Alloc& a, size_type n);<br/>
<tab>[[nodiscard]] static <ins>constexpr </ins>pointer allocate(Alloc& a, size_type n, const_void_pointer hint);<br/>
      <tab>static <ins>constexpr </ins>void deallocate(Alloc& a, pointer p, size_type n);<br/>
      <tab>template&lt;class T, class... Args&gt;<br/>
        <tab><tab>static <ins>constexpr </ins>void construct(Alloc& a, T* p, Args&&... args);<br/>
      <tab>template&lt;class T&gt;<br/>
        <tab><tab>static <ins>constexpr </ins>void destroy(Alloc& a, T* p);<br/>
      <tab>static <ins>constexpr </ins>size_type max_size(const Alloc& a) noexcept;<br/>
      <tab>static <ins>constexpr </ins>Alloc select_on_container_copy_construction(const Alloc& rhs);<br/>

</tt>
</blockquote>      
</p>
		
<p>In [allocator.traits.members] add <tt>constexpr</tt> to all the member function declarations.
<blockquote>
<tt>[[nodiscard]] static <ins>constexpr </ins>pointer allocate(Alloc& a, size_type n);</tt>
<br/><tt>[[nodiscard]] static <ins>constexpr </ins>pointer allocate(Alloc& a, size_type n, const_void_pointer hint); </tt>
<br/><tt>static <ins>constexpr </ins>void deallocate(Alloc& a, pointer p, size_type n); </tt>
<br/><tt>template&lt;class T, class... Args&gt;
 <br/><tab> static <ins>constexpr </ins>void construct(Alloc& a, T* p, Args&&... args); </tt>
<br/><tt>template&lt;class T&gt;
  <br/><tab>static <ins>constexpr </ins>void destroy(Alloc& a, T* p); </tt>
<br/><tt>static <ins>constexpr </ins>size_type max_size(const Alloc& a) noexcept; </tt>
<br/><tt>static <ins>constexpr </ins>Alloc select_on_container_copy_construction(const Alloc& rhs); </tt>

<br/>
</blockquote>
</p>
<p>In [default.allocator]/1 add <tt>constexpr</tt> to the destructor,
the copy assignment operator,
and the <tt>allocate</tt> and <tt>deallocate</tt> members.</p>

<blockquote><tt>
			<tab>namespace std {<br/>
			<tab><tab>template&lt;class T&gt; class allocator {<br/>
			<tab><tab>&nbsp;&nbsp;&nbsp;public:<br/>
			<tab><tab><tab>using value_type = T;<br/>
   <tab><tab><tab> using size_type = size_t;<br/>
   <tab><tab><tab> using difference_type = ptr_diff_t;<br/>
   <tab><tab><tab> using propagate_on_container_move_assignment = true_type;<br/>
   <tab><tab><tab> using is_always_equal                        = true_type;<br/>
   <tab><tab><tab> constexpr allocator() noexcept;<br/>
   <tab><tab><tab> constexpr allocator(const allocator&amp;) noexcept;<br/>
   <tab><tab><tab> template&lt;class U&gt; constexpr allocator(const allocator&lt;U&gt;&amp;) noexcept;<br/>
   <tab><tab><tab><ins>constexpr </ins>~allocator();<br/>
<tab><tab><tab><ins>constexpr </ins>allocator&amp; operator=(const allocator&amp;) = default;<br/>
<tab><tab><tab>[[nodiscard]] <ins>constexpr </ins>T* allocate(size_t n);<br/>
<tab><tab><tab><ins>constexpr </ins>void deallocate(T* p, size_t n);<br/>
<tab><tab> };<br/>
<tab>}
</tt></blockquote>


<p>In [allocator.members] paragraph 1, add <tt>constexpr</tt> to the
declaration of <tt>allocate</tt>.</p>
<blockquote><tt>
<tab>[[nodiscard]] <ins>constexpr </ins>T* allocate(size_t n);<br/>
</tt></blockquote>

<p>In [allocator.members] parapgraph 4, add <tt>constexpr</tt> to the
declaration of <tt>deallocate</tt>.</p>
<blockquote><tt>
<tab><ins>constexpr </ins>void deallocate(T* p, size_t n);<br/>
</tt></blockquote>


<p>In [allocator.globals] declare both comparison operators to be
<tt>constexpr</tt>.</p>

<blockquote><tt>
<tab>template&lt;class T, class U&gt;<br/>
<tab>&nbsp;&nbsp;<ins>constexpr </ins>bool operator==(const allocator&lt;T&gt;&amp;, const allocator&lt;U&gt;&amp;) noexcept;
</tt></blockquote>
<blockquote>
1<tab>&nbsp;&nbsp;&nbsp;&nbsp;Returns: <tt>true</tt>.
</blockquote>

<blockquote><tt>
<tab>template&lt;class T, class U&gt;<br/>
<tab>&nbsp;&nbsp;<ins>constexpr </ins>bool operator!=(const allocator&lt;T&gt;&amp;, const allocator&lt;U&gt;&amp;) noexcept;
</tt></blockquote>
<blockquote>
2<tab>&nbsp;&nbsp;&nbsp;&nbsp;Returns: <tt>false</tt>.
</blockquote>

<p> Add to Table 17: Feature-test macros with the appropriate value:
<br/>
<br><ins><tt> __cpp_constexpr_dynamic_alloc  </tt></ins>
</p>
<p> Add to Table 36: Standard library feature-test macros with the appropriate value:
<br/>
<br><ins><tt> __cpp_lib_constexpr_dynamic_alloc <tab/>&lt;memory&gt; </tt></ins>
</p>
</body></html>

