Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 153 additions & 4 deletions xml/issue4251.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

<issue num="4251" status="New">
<title>Move assignment for `indirect` unnecessarily requires copy construction</title>
<section><sref ref="[indirect.asgn]"/></section>
<section>
<sref ref="[indirect.assign]"/>
<sref ref="[polymorphic.assign]"/>
</section>
<submitter>Jonathan Wakely</submitter>
<date>01 May 2025</date>
<priority>1</priority>
Expand Down Expand Up @@ -46,15 +49,14 @@ Set priority to 1 after reflector poll.
Similar change needed for `std::polymorphic`.
</p>

</discussion>

<resolution>
<superseded>
<p>
This wording is relative to <paper num="N5008"/>.
</p>
<ol>

<li><p>Modify <sref ref="[indirect.asgn]"/> as indicated:</p>
<li><p>Modify <sref ref="[indirect.assign]"/> as indicated:</p>
<blockquote>
<pre><code>
constexpr indirect&amp; operator=(indirect&amp;&amp; other)
Expand Down Expand Up @@ -122,6 +124,153 @@ the allocator in `*this` is replaced with a copy of the allocator in `other`.
</blockquote>
</blockquote>
</li>
</ol>
</superseded>

<note>2025-11-03; Tomasz provides wording.</note>
</discussion>

<resolution>
<p>
This wording is relative to <paper num="N5008"/>.
</p>
<ol>

<li><p>Modify <sref ref="[indirect.assign]"/> as indicated:</p>
<blockquote>
<pre><code>
constexpr indirect&amp; operator=(indirect&amp;&amp; other)
noexcept(allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value ||
allocator_traits&lt;Allocator&gt;::is_always_equal::value);
</code></pre>
<blockquote>
<p>
-5- <i>Mandates</i>:
<ins>
If
<code>allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value</code>
is `false`
and
<code>allocator_traits&lt;Allocator&gt;::is_always_equal::value</code>
is `false`,
</ins>
<code>is_<del>copy</del><ins>move</ins>_constructible_t&lt;T&gt;</code> is `true`.
</p>
<p>
-6- <i>Effects</i>:
If `addressof(other) == this` is `true`, there are no effects.
Otherwise:
<ol style="list-style-type:none">
<li>(6.1) &mdash;
The allocator needs updating if
<code>allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value</code>
is `true`.
</li>
<li>(6.2) &mdash;
If `other` is valueless, `*this` becomes valueless<del> and the owned object
in `*this`, if any, is destroyed using
<code>allocator_traits&lt;Allocator&gt;::destroy</code>
and then the storage is deallocated</del>.
</li>
<li>(6.3) &mdash;
Otherwise,
<ins>if the allocator needs updating or</ins>
if <code><i>alloc</i> == other.<i>alloc</i></code> is `true`,
<del>
swaps the owned objects in `*this` and `other`;
the owned object in `other`, if any, is then destroyed using
<code>allocator_traits&lt;Allocator&gt;::destroy</code>
and then the storage is deallocated
</del>
<ins>`*this` takes ownership of the owned object of `other`</ins>.
</li>
<li>(6.4) &mdash;
Otherwise, constructs a new owned object with the owned object of `other`
as the argument as an rvalue, using <del>either</del> the allocator in `*this`
<del>or the allocator in `other` if the allocator needs updating</del>.
</li>
<li>(6.5) &mdash;
The previously owned object in `*this`, if any, is destroyed using
<code>allocator_traits&lt;Allocator&gt;::destroy</code>
and then the storage is deallocated.
</li>
<li>(6.6) &mdash;
If the allocator needs updating,
the allocator in `*this` is replaced with a copy of the allocator in `other`.
</li>
</ol>
</p>
<p>-7- <i>Postcondition</i>: `other` is valueless.</p>
</blockquote>
</blockquote>
</li>

<li><p>Modify <sref ref="[polymorphic.assign]"/> as indicated:</p>
<blockquote>
<pre><code>
constexpr polymorphic&amp; operator=(polymorphic&amp;&amp; other)
noexcept(allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value ||
allocator_traits&lt;Allocator&gt;::is_always_equal::value);
</code></pre>
<blockquote>
<p>
-5- <i>Mandates</i>:
If
<ins>
<code>allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value</code>
is `false`
and
</ins>
<code>allocator_traits&lt;Allocator&gt;::is_always_equal::value</code>
is `false`,
`T` is complete type.
</p>
<p>
-6- <i>Effects</i>:
If `addressof(other) == this` is `true`, there are no effects.
Otherwise:
<ol style="list-style-type:none">
<li>(6.1) &mdash;
The allocator needs updating if
<code>allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value</code>
is `true`.
</li>
<li><ins>(6.?) &mdash; If `other` is valueless, `*this` becomes valueless.</ins></li>
<li>(6.2) &mdash;
<ins>Otherwise, if the allocator needs updating or</ins><del>If</del>
<code><i>alloc</i> == other.<i>alloc</i></code> is `true`,
<del>
swaps the owned objects in `*this` and `other`;
the owned object in `other`, if any, is then destroyed using
<code>allocator_traits&lt;Allocator&gt;::destroy</code>
and then the storage is deallocated
</del>
<ins>`*this` takes ownership of the owned object of `other`</ins>.
</li>
<li>(6.3) &mdash;
<del>Otherwise, if `alloc != other.alloc` is `true`; if `other` is not valueless,
a new owned object is constructed in `*this` using <tt>allocator_traits::construct</tt>
with the owned object from</del>
<ins>Otherwise, constructs a new owned object with the owned object of</ins>
`other` as the argument as an rvalue, using <del>either</del> the allocator in `*this`
<del>or the allocator in `other` if the allocator needs updating</del>.
</li>
<li>(6.4) &mdash;
The previously owned object in `*this`, if any, is destroyed using
<code>allocator_traits&lt;Allocator&gt;::destroy</code>
and then the storage is deallocated.
</li>
<li>(6.5) &mdash;
If the allocator needs updating,
the allocator in `*this` is replaced with a copy of the allocator in `other`.
</li>
</ol>
</p>
[&hellip;]
</blockquote>
</blockquote>
</li>

</ol>
</resolution>

Expand Down
87 changes: 84 additions & 3 deletions xml/issue4316.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ Set priority to 1 after reflector poll.
</p>

<note>2025-10-27; Tomasz provides wording.</note>
</discussion>

<resolution>
<superseded>

<p>
This wording is relative to <paper num="N5014"/>.
Expand Down Expand Up @@ -126,6 +124,89 @@ context, the program is ill-formed. &mdash; <i>end note</i>]
</li>
</ol>

</superseded>

<note>2025-10-27; Reflector comments.</note>
<p>
We lost definition of `Z`. Use <tt><i>TARG-SPLICE</i>([:Args:])...</tt>.
</p>

<note>2025-11-03; Tomasz provides wording.</note>
</discussion>

<resolution>

<p>
This wording is relative to <paper num="N5014"/>.
</p>

<ol>

<li><p>Modify <sref ref="[meta.reflection.substitute]"/> as indicated:</p>

<blockquote>
<blockquote>
<p>
<ins>-1- For value `x` of type `info`, and prvalue constant expression `X` that computes the
reflection held by `x`, let <tt><i>TARG-SPLICE</i>(x)</tt> be:</ins>
<ul>
<li><ins>-1.1- <tt>template [: X :]</tt> if `is_template(x)` is `true`, otherwise</ins></li>
<li><ins>-1.2- <tt>typename [: X :]</tt> if `is_type(x)` is `true`, otherwise</ins></li>
<li><ins>-1.3- <tt>([: X :])</tt></ins></li>
</ul>
</p>
</blockquote>

<pre>
template&lt;reflection_range R = initializer_list&lt;info&gt;&gt;
consteval bool can_substitute(info templ, R&amp;&amp; arguments);
</pre>
<blockquote>
<p>
-1- Let `Z` be the template represented by `templ` and let `Args...` be a
sequence of prvalue constant expressions that compute the reflections held by
the elements of `arguments`, in order.
</p>
<p>
-2- <i>Returns</i>: `true` if <tt>Z&lt;<ins><i>TARG-SPLICE</i>(</ins>[:Args:]<ins>)</ins>...&gt;</tt> is a valid <i>template-id</i>
(<sref ref="[temp.names]"/>) that does not name a function whose type contains an undeduced placeholder type.
Otherwise, `false`.
</p>
<p>
-3- <i>Throws</i>: `meta::exception` unless `templ` represents a template, and every reflection
in `arguments` represents a construct usable as a template argument (<sref ref="[temp.arg]"/>).
</p>
<p>
-4- [<i>Note</i>: If forming <tt>Z&lt;<ins><i>TARG-SPLICE</i>(</ins>[:Args:]<ins>)</ins>...&gt;</tt>
leads to a failure outside of the immediate context, the program is ill-formed. &mdash; <i>end note</i>]
</p>
</blockquote>

<pre>
template&lt;reflection_range R = initializer_list&lt;info&gt;&gt;
consteval info substitute(info templ, R&amp;&amp; arguments);
</pre>
<blockquote>
<p>
-5- Let `Z` be the template represented by `templ` and let `Args...` be a
sequence of prvalue constant expressions that compute the reflections held by
the elements of `arguments`, in order.
</p>
<p>
-6- <i>Returns</i>: <tt>Z&lt;<ins><i>TARG-SPLICE</i>(</ins>[:Args:]<ins>)</ins>...&gt;</tt>.</p>
<p>
-7- <i>Throws</i>: `meta::exception` unless `can_substitute(templ, arguments)` is `true`.
</p>
<p>
-8- [<i>Note</i>: If forming <tt>Z&lt;<ins><i>TARG-SPLICE</i>(</ins>[:Args:]<ins>)</ins>...&gt;</tt>
leads to a failure outside of the immediate context, the program is ill-formed. &mdash; <i>end note</i>]
</p>
</blockquote>

</blockquote>
</li>
</ol>

</resolution>

</issue>
Loading