Topic: Stricter semantics for moved into objects with operator=(T&&)
Author: Matthew Fioravante <fmatthew5876@gmail.com>
Date: Wed, 8 Oct 2014 18:48:37 -0700 (PDT)
Raw View
------=_Part_1006_1700683564.1412819317168
Content-Type: text/plain; charset=UTF-8
Currently, the standard says that when you move from an object, its left in
a valid but unspecified state. This allows a subtly less than desirable
implementation for op=(T&&).
In this talk:
https://www.youtube.com/watch?v=ECoLo17nG5c
The presenter shows a potential implementation of move assignment using
swap().
template <typename T>
class vector {
public:
vector& operator=(vector<T>&&o) {
using std::swap;
swap(_data, o._data);
swap(_size, o._size);
swap(_capacity, o._capacity);
}
private:
T* _data;
size_t _size;
size_t _capacity;
};
I believe this is standards compliant, but in my opinion this is a very bad
idea. When I see an assignment like a = b, I expect that whatever resources
held by a before the assignment will be immediately released at the point
of assignment.
With this swap implementation, the resources are not released but merely
transferred to b, and the b object may stick around for a while.
Consider the following hypothetical example:
vector<int> a = /* 1GB of data */
vector<int> b = /* 1GB of data */
a = std::move(b);
vector<int>c = /* 1GB of data */
At the end of this sequence, 3GB of memory will be in use and on a memory
constrained system, this could cause an out of memory error simply because
we used move semantics (which is supposed to be a optimization!) instead of
copy.
One of the greatest strengths of C++ is that you have exact control over
resource allocation. Unlike garbage collected languages which are free to
allocate resources however like they and stall your program for collection
whenever they want, in C++ we are given strict control.
By using the move in terms of swap implementation, we break that guarantee.
Perhaps there should be some stronger wording in the standard with regards
to the STL types to disallow op=(T&&) implementations of the standard
containers using swap. Namely that if you overwrite their contents with a
move assignment, the old resources are guaranteed to be released, just like
copy. Another way to achieve this guarantee to is say that instead of the
moved from object's state being valid but unspecified, we can say that it
is empty or in a default constructed state.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_1006_1700683564.1412819317168
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">Currently, the standard says that when you move from an ob=
ject, its left in a valid but unspecified state. This allows a subtly less =
than desirable implementation for op=3D(T&&).<div><br>In this talk:=
</div><div>https://www.youtube.com/watch?v=3DECoLo17nG5c<br></div><div><br>=
</div><div>The presenter shows a potential implementation of move assignmen=
t using swap().</div><div><br></div><div><div class=3D"prettyprint" style=
=3D"background-color: rgb(250, 250, 250); border: 1px solid rgb(187, 187, 1=
87); word-wrap: break-word;"><code class=3D"prettyprint"><div class=3D"subp=
rettyprint"><span style=3D"color: #008;" class=3D"styled-by-prettify">templ=
ate</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span=
><span style=3D"color: #660;" class=3D"styled-by-prettify"><</span><span=
style=3D"color: #008;" class=3D"styled-by-prettify">typename</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify"> T</span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">></span><span style=3D"color:=
#000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #008;=
" class=3D"styled-by-prettify">class</span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"> vector </span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">{</span><span style=3D"color: #000;" class=3D"style=
d-by-prettify"><br> </span><span style=3D"color: #008;" class=3D"styl=
ed-by-prettify">public</span><span style=3D"color: #660;" class=3D"styled-b=
y-prettify">:</span><span style=3D"color: #000;" class=3D"styled-by-prettif=
y"><br> vector</span><span style=3D"color: #660;" class=3D"sty=
led-by-prettify">&</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-prettif=
y">operator</span><span style=3D"color: #660;" class=3D"styled-by-prettify"=
>=3D(</span><span style=3D"color: #000;" class=3D"styled-by-prettify">vecto=
r</span><span style=3D"color: #660;" class=3D"styled-by-prettify"><</spa=
n><span style=3D"color: #000;" class=3D"styled-by-prettify">T</span><span s=
tyle=3D"color: #660;" class=3D"styled-by-prettify">>&&</span><sp=
an style=3D"color: #000;" class=3D"styled-by-prettify">o</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">)</span><span style=3D"color=
: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #660;" =
class=3D"styled-by-prettify">{</span><span style=3D"color: #000;" class=3D"=
styled-by-prettify"><br> </span><span style=3D"color: #=
008;" class=3D"styled-by-prettify">using</span><span style=3D"color: #000;"=
class=3D"styled-by-prettify"> std</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">::</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify">swap</span><font color=3D"#666600"><span style=3D"color: #6=
60;" class=3D"styled-by-prettify">;</span><span style=3D"color: #000;" clas=
s=3D"styled-by-prettify"><br></span></font><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"> swap</span><span style=3D"co=
lor: #660;" class=3D"styled-by-prettify">(</span><span style=3D"color: #000=
;" class=3D"styled-by-prettify">_data</span><span style=3D"color: #660;" cl=
ass=3D"styled-by-prettify">,</span><span style=3D"color: #000;" class=3D"st=
yled-by-prettify"> o</span><span style=3D"color: #660;" class=3D"styled-by-=
prettify">.</span><span style=3D"color: #000;" class=3D"styled-by-prettify"=
>_data</span><span style=3D"color: #660;" class=3D"styled-by-prettify">);</=
span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br> &=
nbsp; swap</span><span style=3D"color: #660;" class=3D"styled-by-pre=
ttify">(</span><span style=3D"color: #000;" class=3D"styled-by-prettify">_s=
ize</span><span style=3D"color: #660;" class=3D"styled-by-prettify">,</span=
><span style=3D"color: #000;" class=3D"styled-by-prettify"> o</span><span s=
tyle=3D"color: #660;" class=3D"styled-by-prettify">.</span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify">_size</span><span style=3D"color:=
#660;" class=3D"styled-by-prettify">);</span><span style=3D"color: #000;" =
class=3D"styled-by-prettify"><br> swap</span><span styl=
e=3D"color: #660;" class=3D"styled-by-prettify">(</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify">_capacity</span><span style=3D"color=
: #660;" class=3D"styled-by-prettify">,</span><span style=3D"color: #000;" =
class=3D"styled-by-prettify"> o</span><span style=3D"color: #660;" class=3D=
"styled-by-prettify">.</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify">_capacity</span><span style=3D"color: #660;" class=3D"styled-by=
-prettify">);</span><span style=3D"color: #000;" class=3D"styled-by-prettif=
y"><br> </span><span style=3D"color: #660;" class=3D"styled-by=
-prettify">}</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"><br> </span><span style=3D"color: #008;" class=3D"styled-by-prettif=
y">private</span><span style=3D"color: #660;" class=3D"styled-by-prettify">=
:</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br> =
; T</span><span style=3D"color: #660;" class=3D"styled-by-prettify">=
*</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> _data</s=
pan><span style=3D"color: #660;" class=3D"styled-by-prettify">;</span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"><br> size=
_t _size</span><span style=3D"color: #660;" class=3D"styled-by-prettify">;<=
/span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br> =
size_t _capacity</span><span style=3D"color: #660;" class=3D"styled-=
by-prettify">;</span><span style=3D"color: #000;" class=3D"styled-by-pretti=
fy"><br></span><span style=3D"color: #660;" class=3D"styled-by-prettify">};=
</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span=
><font color=3D"#666600"></font></div></code></div><br>I believe this is st=
andards compliant, but in my opinion this is a very bad idea. When I see an=
assignment like a =3D b, I expect that whatever resources held by a before=
the assignment will be immediately released at the point of assignment.</d=
iv><div><br></div><div>With this swap implementation, the resources are not=
released but merely transferred to b, and the b object may stick around fo=
r a while.</div><div><br></div><div>Consider the following hypothetical exa=
mple:</div><div><div class=3D"prettyprint" style=3D"background-color: rgb(2=
50, 250, 250); border: 1px solid rgb(187, 187, 187); word-wrap: break-word;=
"><code class=3D"prettyprint"><div class=3D"subprettyprint"><span style=3D"=
color: #000;" class=3D"styled-by-prettify">vector</span><span style=3D"colo=
r: #080;" class=3D"styled-by-prettify"><int></span><span style=3D"col=
or: #000;" class=3D"styled-by-prettify"> a </span><span style=3D"color: #66=
0;" class=3D"styled-by-prettify">=3D</span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"> </span><span style=3D"color: #800;" class=3D"sty=
led-by-prettify">/* 1GB of data */</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"><br>vector</span><span style=3D"color: #080;" class=
=3D"styled-by-prettify"><int></span><font color=3D"#666600"><span sty=
le=3D"color: #000;" class=3D"styled-by-prettify"> b </span><span style=3D"c=
olor: #660;" class=3D"styled-by-prettify">=3D</span><span style=3D"color: #=
000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #800;" cla=
ss=3D"styled-by-prettify">/* 1GB of data */</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify"><br>a </span><span style=3D"color: #660;" =
class=3D"styled-by-prettify">=3D</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"> std</span><span style=3D"color: #660;" class=3D"st=
yled-by-prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-=
prettify">move</span><span style=3D"color: #660;" class=3D"styled-by-pretti=
fy">(</span><span style=3D"color: #000;" class=3D"styled-by-prettify">b</sp=
an><span style=3D"color: #660;" class=3D"styled-by-prettify">);</span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"><br>vector</span><span=
style=3D"color: #080;" class=3D"styled-by-prettify"><int></span><spa=
n style=3D"color: #000;" class=3D"styled-by-prettify">c </span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">=3D</span><span style=3D"col=
or: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #800;=
" class=3D"styled-by-prettify">/* 1GB of data */</span></font></div></code>=
</div><div><br></div>At the end of this sequence, 3GB of memory will be in =
use and on a memory constrained system, this could cause an out of memory e=
rror simply because we used move semantics (which is supposed to be a optim=
ization!) instead of copy.</div><div><br></div><div>One of the greatest str=
engths of C++ is that you have exact control over resource allocation. Unli=
ke garbage collected languages which are free to allocate resources however=
like they and stall your program for collection whenever they want, in C++=
we are given strict control.</div><div><br></div><div>By using the move in=
terms of swap implementation, we break that guarantee.</div><div><br></div=
><div>Perhaps there should be some stronger wording in the standard with re=
gards to the STL types to disallow op=3D(T&&) implementations of th=
e standard containers using swap. Namely that if you overwrite their conten=
ts with a move assignment, the old resources are guaranteed to be released,=
just like copy. Another way to achieve this guarantee to is say that inste=
ad of the moved from object's state being valid but unspecified, we can say=
that it is empty or in a default constructed state.<br><br></div></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_1006_1700683564.1412819317168--
.
Author: David Krauss <potswa@gmail.com>
Date: Thu, 9 Oct 2014 11:57:53 +0800
Raw View
--Apple-Mail=_78983927-3BEB-4226-88F9-982B336EF5A7
Content-Type: text/plain; charset=ISO-8859-1
On 2014-10-09, at 9:48 AM, Matthew Fioravante <fmatthew5876@gmail.com> wrote:
> At the end of this sequence, 3GB of memory will be in use and on a memory constrained system, this could cause an out of memory error simply because we used move semantics (which is supposed to be a optimization!) instead of copy.
Whenever you actually need to free memory, you need to swap with an empty object. Even clear() won't do.
Having capacity in moved-from or otherwise empty vectors is an important optimization, because they are likely to refer to hot cache lines.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
--Apple-Mail=_78983927-3BEB-4226-88F9-982B336EF5A7
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=ISO-8859-1
<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html charset=
=3Dwindows-1252"></head><body style=3D"word-wrap: break-word; -webkit-nbsp-=
mode: space; -webkit-line-break: after-white-space;"><br><div><div>On 2014&=
ndash;10–09, at 9:48 AM, Matthew Fioravante <<a href=3D"mailto:fma=
tthew5876@gmail.com">fmatthew5876@gmail.com</a>> wrote:</div><br class=
=3D"Apple-interchange-newline"><blockquote type=3D"cite"><div style=3D"font=
-family: Helvetica; font-size: 12px; font-style: normal; font-variant: norm=
al; font-weight: normal; letter-spacing: normal; line-height: normal; orpha=
ns: auto; text-align: start; text-indent: 0px; text-transform: none; white-=
space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: =
0px;">At the end of this sequence, 3GB of memory will be in use and on a me=
mory constrained system, this could cause an out of memory error simply bec=
ause we used move semantics (which is supposed to be a optimization!) inste=
ad of copy.</div></blockquote></div><br><div>Whenever you actually need to =
free memory, you need to swap with an empty object. Even clear() won’=
t do.</div><div><br></div><div>Having capacity in moved-from or otherwise e=
mpty vectors is an important optimization, because they are likely to refer=
to hot cache lines.</div></body></html>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
--Apple-Mail=_78983927-3BEB-4226-88F9-982B336EF5A7--
.
Author: Marc Mutz <marc.mutz@kdab.com>
Date: Thu, 9 Oct 2014 10:10:11 +0200
Raw View
Hi Matthew,
On Thursday 09 October 2014 03:48:37 Matthew Fioravante wrote:
[...]
> I believe this is standards compliant, but in my opinion this is a very
> bad idea. When I see an assignment like a = b, I expect that whatever
> resources held by a before the assignment will be immediately released at
> the point of assignment.
>
> With this swap implementation, the resources are not released but merely
> transferred to b, and the b object may stick around for a while.
[...]
The common case is that the moved-from object does _not_ stick around for very
much longer. It's telling that you need to use an example that uses explicit
lvalue -> xvalue conversion to demonstrate a problem.
This is something that has come up several times. I wasn't involved in the
standardisation process, but we have the same problem in Qt. We use swap
there, too, most of the times, since most of our classes are out-of-line due
to binary compatibility guarantees (no, don't take that bait). Swapping is the
only thing you can do inline (and you *so* want move assignment to be
inline...) if you're dealing with opaque pointers. The ctors and dtors are
out-of-line, so they're the unique points of allocation and deallocation.
A similar issue exists with move ctors. In a pimpl'ed class, unless you want
to allow a nullptr pImpl (which puts you into the realm of Destructive Move,
unless you want every member function to check for pImpl == nullptr), the only
way to implement it inline is to delegate to the default ctor (which hopefully
doesn't allocate memory) and then swap this' contents with the argument of the
move ctor.
So, I'm quite happy that the standard gives us this leeway.
In this case:
> vector<int> a = /* 1GB of data */
> vector<int> b = /* 1GB of data */
> a = std::move(b);
> vector<int>c = /* 1GB of data */
the solution is to simply re-use b:
b = /* 1GB of data */
or to properly scope b:
vector<int> a = /* 1GB of data */
{
vector<int> b = /* 1GB of data */
a = std::move(b);
}
vector<int>c = /* 1GB of data */
or, as David mentioned, to swap b with a std::vector<int>() to shed excess
capacity.
Thanks,
Marc Mutz
--
Qt Developer Days 2014 - October 6 - 8 at BCC, Berlin
Marc Mutz <marc.mutz@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
www.kdab.com || Germany +49-30-521325470 || Sweden (HQ) +46-563-540090
KDAB - Qt Experts - Platform-Independent Software Solutions
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.