Skip to content

Commit 1addbae

Browse files
committed
New issue: Move assignment for indirect unnecessarily requires copy construction
1 parent 2399890 commit 1addbae

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

xml/issue4251.xml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4251" status="New">
5+
<title>Move assignment for `indirect` unnecessarily requires copy construction</title>
6+
<section><sref ref="[indirect.asgn]"/></section>
7+
<submitter>Jonathan Wakely</submitter>
8+
<date>01 May 2025</date>
9+
<priority>99</priority>
10+
11+
<discussion>
12+
<p>
13+
The move assignment operator for `indirect` says:
14+
<blockquote>
15+
<i>Mandates</i>: <code>is_copy_constructible_t&lt;T&gt;</code> is `true`.
16+
</blockquote>
17+
18+
However, the only way it ever construct an object is:
19+
<blockquote>
20+
constructs a new owned object with the owned object of `other` as the argument
21+
as an rvalue
22+
</blockquote>
23+
and that only ever happens when <code><i>alloc</i> == other.<i>alloc</i></code>
24+
is false.
25+
</p>
26+
<p>
27+
It seems like we should require `is_move_constructible_v` instead,
28+
and only if the allocator traits mean we need to construct an object.
29+
</p>
30+
<p>
31+
Additionally, the noexcept-specifier for the move assignment doesn't match
32+
the effects. The noexcept-specifier says it can't throw if POCMA is true,
33+
but nothing in the effects says that ownership can be transferred in that case.
34+
We only do a non-throwing transfer when the allocators are equal.
35+
I think we should also transfer ownership when
36+
</p>
37+
</discussion>
38+
39+
<resolution>
40+
<p>
41+
This wording is relative to <paper num="N5008"/>.
42+
</p>
43+
<ol>
44+
45+
<li><p>Modify <sref ref="[indirect.asgn]"/> as indicated:</p>
46+
<blockquote>
47+
<pre><code>
48+
constexpr indirect&amp; operator=(indirect&amp;&amp; other)
49+
noexcept(allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value ||
50+
allocator_traits&lt;Allocator&gt;::is_always_equal::value);
51+
</code></pre>
52+
<blockquote>
53+
<p>
54+
-5- <i>Mandates</i>:
55+
<ins>
56+
If
57+
<code>allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value</code>
58+
is `false`
59+
and
60+
<code>allocator_traits&lt;Allocator&gt;::is_always_equal::value</code>
61+
is `false`,
62+
</ins>
63+
<code>is_<del>copy</del><ins>move</ins>_constructible_t&lt;T&gt;</code> is `true`.
64+
</p>
65+
<p>
66+
-6- <i>Effects</i>:
67+
If `addressof(other) == this` is `true`, there are no effects.
68+
Otherwise:
69+
<ol style="list-style-type:none">
70+
<li>(6.1) &mdash;
71+
The allocator needs updating if
72+
<code>allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value</code>
73+
is `true`.
74+
</li>
75+
<li>(6.2) &mdash;
76+
If `other` is valueless, `*this` becomes valueless<del> and the owned object
77+
in `*this`, if any, is destroyed using
78+
<code>allocator_traits&lt;Allocator&gt;::destroy</code>
79+
and then the storage is deallocated</del>.
80+
</li>
81+
<li>(6.3) &mdash;
82+
Otherwise,
83+
<ins>if the allocator needs updating or</ins>
84+
if <code><i>alloc</i> == other.<i>alloc</i></code> is `true`,
85+
<del>
86+
swaps the owned objects in `*this` and `other`;
87+
the owned object in `other`, if any, is then destroyed using
88+
<code>allocator_traits&lt;Allocator&gt;::destroy</code>
89+
and then the storage is deallocated
90+
</del>
91+
<ins>`*this` takes ownership of the owned object of `other`</ins>.
92+
</li>
93+
<li>(6.4) &mdash;
94+
Otherwise, constructs a new owned object with the owned object of `other`
95+
as the argument as an rvalue, using either the allocator in `*this`
96+
or the allocator in `other` if the allocator needs updating.
97+
</li>
98+
<li>(6.5) &mdash;
99+
The previously owned object in `*this`, if any, is destroyed using
100+
<code>allocator_traits&lt;Allocator&gt;::destroy</code>
101+
and then the storage is deallocated.
102+
</li>
103+
<li>(6.6) &mdash;
104+
If the allocator needs updating,
105+
the allocator in `*this` is replaced with a copy of the allocator in `other`.
106+
</li>
107+
</ol>
108+
</p>
109+
<p>-7- <i>Postcondition</i>: `other` is valueless.</p>
110+
</blockquote>
111+
</blockquote>
112+
</li>
113+
</ol>
114+
</resolution>
115+
116+
</issue>

0 commit comments

Comments
 (0)