Topic: Is `std::exchange(obj, {})` common enough to


Author: Marc Mutz <marc.mutz@kdab.com>
Date: Sun, 17 Dec 2017 01:47:40 +0100
Raw View
On 2017-12-16 00:19, Nicol Bolas wrote:
[...]
> I don't mind people using this kind of `exchange` idiom. But if we
> have a function specifically for this, then we are effectively
> encouraging people to do it. Even when it's not a good idea.
>
> Also, remember that `exchange` does two things. It moves from the
> original, but it also move_ into_ the original. In the first case, you
> don't care that `m_items` gets default initialized; you just want it
> to be moved-from.

The first example was actually meant as a shortcut for

    auto items = std::move(m_items);
    m_items.clear(); // or = {}
    for (auto &item : items)

which is a pattern common enough to be annoying without std::exchange
(or with CoW containers which detach in ranged for-loops *cough* Qt
*cough*).

    for (auto &item : std::take(m_items)) // using the shortest function
name as a placeholder

> In the other two cases, default initialization is
> not merely a default; it's a fundamental part of the algorithm.
>
> So you would need two functions: one that will move a
> default-constructed prvalue into the old object (and thus impose both
> DefaultConstructible and MoveAssignable requirements on it) and one
> which does not.

I considered std::take (which does) and std::move (which does not) to be
those two functions.

I said "supplement std::move", not "replace std::move". The problem is
that the std uses the hand-waving "valid, but unspecified state" to
define the moved-from state, and that forces people to call clear() or
assign {} to a moved-from object to get it back into a defined state.

std::take() would leave the moved-from object in a defined state (the -
help me - value-initialized(?) one), while std::move() leaves the
moved-from object in an unspecified state, which is ok if you don't
indent to use the moved-from object later, except to assign a new value
or destroy it, but gets in the way otherwise.

If std::take() was function separate from std::exchange(), stdlib
implementations could overload it to avoid the assignment if they know
their implementation reaches the default-constructed state for
moved-from objects:

     template <typename...Args>
     auto take(std::vector<Args...> &v) {
         return std::move(v); // post: v.empty() (I believe this holds
for libstd++'s vector, at least)
     }

     template <typename...Args>
     auto take(std::basic_string<Args...> &s) {
         auto r = std::move(s);
         s.clear(); // deal with SSO
         return r;
     }

Thanks,
Marc



--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/92503170b7dac21ab086362120b27a24%40kdab.com.

.


Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sat, 16 Dec 2017 19:11:22 -0800 (PST)
Raw View
------=_Part_9538_1983576986.1513480282239
Content-Type: multipart/alternative;
 boundary="----=_Part_9539_566262672.1513480282239"

------=_Part_9539_566262672.1513480282239
Content-Type: text/plain; charset="UTF-8"



On Saturday, December 16, 2017 at 7:51:18 PM UTC-5, Marc Mutz wrote:
>
> On 2017-12-16 00:19, Nicol Bolas wrote:
> [...]
> > I don't mind people using this kind of `exchange` idiom. But if we
> > have a function specifically for this, then we are effectively
> > encouraging people to do it. Even when it's not a good idea.
> >
> > Also, remember that `exchange` does two things. It moves from the
> > original, but it also move_ into_ the original. In the first case, you
> > don't care that `m_items` gets default initialized; you just want it
> > to be moved-from.
>
> The first example was actually meant as a shortcut for
>
>     auto items = std::move(m_items);
>     m_items.clear(); // or = {}
>     for (auto &item : items)
>
> which is a pattern common enough to be annoying without std::exchange
>

This exemplifies my point about the need for two functions. Do you* really*
need `m_items` to be cleared, or do you just need to destroy its contents?
There are times when you genuinely need the clearing, and there are times
when you genuinely don't care.

(or with CoW containers which detach in ranged for-loops *cough* Qt
> *cough*).
>
>     for (auto &item : std::take(m_items)) // using the shortest function
> name as a placeholder
>
> > In the other two cases, default initialization is
> > not merely a default; it's a fundamental part of the algorithm.
> >
> > So you would need two functions: one that will move a
> > default-constructed prvalue into the old object (and thus impose both
> > DefaultConstructible and MoveAssignable requirements on it) and one
> > which does not.
>
> I considered std::take (which does) and std::move (which does not) to be
> those two functions.
>

But, as you pointed out, `std::move` does not move anything; it's just a
cast. So `std::move` does not satisfy what I said.

The two functions I'm talking about both provoke an actual, honest-to-God
move. There's:

template<typename T>
auto move_and_clear(T &t)
{
  T new_t = std::move(t);
  t = T{};
  return new_t;
}

And there's:

template<typename T>
auto force_move(T &t)
{
  return T(std::move(t));
}

They both provoke a move. But one leaves the object in the "moved-from"
state, while the other leaves it in a very well defined state (assigned
from a default-constructed object).

I said "supplement std::move", not "replace std::move". The problem is
> that the std uses the hand-waving "valid, but unspecified state" to
> define the moved-from state, and that forces people to call clear() or
> assign {} to a moved-from object to get it back into a defined state.
>

And what's wrong with that? Most of the time when you're moving something,
you don't care what state the moved-from object is in. Since more often
than not, you're done with it.

The purpose of `force_move` is to ensure that a move actually happens. Some
functions that you might pass an rvalue reference to may not actually move
from it. But you may no longer want to keep the object's contents around;
you* want* the receiver to take it. So by using `force_move`, you ensure
that either the receiver moved from it or the temporary that was manifested
will destroy the data in the object at the end of that expression.

Either way, by the time the next statement starts, you're guaranteed that
the data is gone.


--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ec0c2616-f0f3-42d6-9d8c-756ee532b850%40isocpp.org.

------=_Part_9539_566262672.1513480282239
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br><br>On Saturday, December 16, 2017 at 7:51:18 PM UTC-5=
, Marc Mutz wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;marg=
in-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">On 2017-12-1=
6 00:19, Nicol Bolas wrote:
<br>[...]
<br>&gt; I don&#39;t mind people using this kind of `exchange` idiom. But i=
f we
<br>&gt; have a function specifically for this, then we are effectively
<br>&gt; encouraging people to do it. Even when it&#39;s not a good idea.
<br>&gt;=20
<br>&gt; Also, remember that `exchange` does two things. It moves from the
<br>&gt; original, but it also move_ into_ the original. In the first case,=
 you
<br>&gt; don&#39;t care that `m_items` gets default initialized; you just w=
ant it
<br>&gt; to be moved-from.
<br>
<br>The first example was actually meant as a shortcut for
<br>
<br>=C2=A0 =C2=A0 auto items =3D std::move(m_items);
<br>=C2=A0 =C2=A0 m_items.clear(); // or =3D {}
<br>=C2=A0 =C2=A0 for (auto &amp;item : items)
<br>
<br>which is a pattern common enough to be annoying without std::exchange<b=
r></blockquote><div><br></div><div>This exemplifies my point about the need=
 for two functions. Do you<i> really</i> need `m_items` to be cleared, or d=
o you just need to destroy its contents? There are times when you genuinely=
 need the clearing, and there are times when you genuinely don&#39;t care.<=
/div><div><i><br></i></div><blockquote class=3D"gmail_quote" style=3D"margi=
n: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">(or=
 with CoW containers which detach in ranged for-loops *cough* Qt=20
<br>*cough*).
<br>
<br>=C2=A0 =C2=A0 for (auto &amp;item : std::take(m_items)) // using the sh=
ortest function=20
<br>name as a placeholder
<br>
<br>&gt; In the other two cases, default initialization is
<br>&gt; not merely a default; it&#39;s a fundamental part of the algorithm=
..
<br>&gt;=20
<br>&gt; So you would need two functions: one that will move a
<br>&gt; default-constructed prvalue into the old object (and thus impose b=
oth
<br>&gt; DefaultConstructible and MoveAssignable requirements on it) and on=
e
<br>&gt; which does not.
<br>
<br>I considered std::take (which does) and std::move (which does not) to b=
e=20
<br>those two functions.<br></blockquote><div><br></div><div>But, as you po=
inted out, `std::move` does not move anything; it&#39;s just a cast. So `st=
d::move` does not satisfy what I said.</div><div><br></div><div>The two fun=
ctions I&#39;m talking about both provoke an actual, honest-to-God move. Th=
ere&#39;s:</div><div><br></div><div class=3D"prettyprint" style=3D"border: =
1px solid rgb(187, 187, 187); word-wrap: break-word; background-color: rgb(=
250, 250, 250);"><code class=3D"prettyprint"><div class=3D"subprettyprint">=
<span class=3D"styled-by-prettify" style=3D"color: #008;">template</span><s=
pan class=3D"styled-by-prettify" style=3D"color: #660;">&lt;</span><span cl=
ass=3D"styled-by-prettify" style=3D"color: #008;">typename</span><span clas=
s=3D"styled-by-prettify" style=3D"color: #000;"> T</span><span class=3D"sty=
led-by-prettify" style=3D"color: #660;">&gt;</span><span class=3D"styled-by=
-prettify" style=3D"color: #000;"><br></span><span class=3D"styled-by-prett=
ify" style=3D"color: #008;">auto</span><span class=3D"styled-by-prettify" s=
tyle=3D"color: #000;"> move_and_clear</span><span class=3D"styled-by-pretti=
fy" style=3D"color: #660;">(</span><span class=3D"styled-by-prettify" style=
=3D"color: #000;">T </span><span class=3D"styled-by-prettify" style=3D"colo=
r: #660;">&amp;</span><span class=3D"styled-by-prettify" style=3D"color: #0=
00;">t</span><span class=3D"styled-by-prettify" style=3D"color: #660;">)</s=
pan><span class=3D"styled-by-prettify" style=3D"color: #000;"><br></span><s=
pan class=3D"styled-by-prettify" style=3D"color: #660;">{</span><span class=
=3D"styled-by-prettify" style=3D"color: #000;"><br>=C2=A0 T new_t </span><s=
pan class=3D"styled-by-prettify" style=3D"color: #660;">=3D</span><span cla=
ss=3D"styled-by-prettify" style=3D"color: #000;"> std</span><span class=3D"=
styled-by-prettify" style=3D"color: #660;">::</span><span class=3D"styled-b=
y-prettify" style=3D"color: #000;">move</span><span class=3D"styled-by-pret=
tify" style=3D"color: #660;">(</span><span class=3D"styled-by-prettify" sty=
le=3D"color: #000;">t</span><span class=3D"styled-by-prettify" style=3D"col=
or: #660;">);</span><span class=3D"styled-by-prettify" style=3D"color: #000=
;"><br>=C2=A0 t </span><span class=3D"styled-by-prettify" style=3D"color: #=
660;">=3D</span><span class=3D"styled-by-prettify" style=3D"color: #000;"> =
T</span><span class=3D"styled-by-prettify" style=3D"color: #660;">{};</span=
><span class=3D"styled-by-prettify" style=3D"color: #000;"><br>=C2=A0 </spa=
n><span class=3D"styled-by-prettify" style=3D"color: #008;">return</span><s=
pan class=3D"styled-by-prettify" style=3D"color: #000;"> new_t</span><span =
class=3D"styled-by-prettify" style=3D"color: #660;">;</span><span class=3D"=
styled-by-prettify" style=3D"color: #000;"><br></span><span class=3D"styled=
-by-prettify" style=3D"color: #660;">}</span></div></code></div><div><br></=
div><div>And there&#39;s:</div><div><br></div><div class=3D"prettyprint" st=
yle=3D"border: 1px solid rgb(187, 187, 187); word-wrap: break-word; backgro=
und-color: rgb(250, 250, 250);"><code class=3D"prettyprint"><div class=3D"s=
ubprettyprint"><span class=3D"styled-by-prettify" style=3D"color: #008;">te=
mplate</span><span class=3D"styled-by-prettify" style=3D"color: #660;">&lt;=
</span><span class=3D"styled-by-prettify" style=3D"color: #008;">typename</=
span><span class=3D"styled-by-prettify" style=3D"color: #000;"> T</span><sp=
an class=3D"styled-by-prettify" style=3D"color: #660;">&gt;</span><span cla=
ss=3D"styled-by-prettify" style=3D"color: #000;"><br></span><span class=3D"=
styled-by-prettify" style=3D"color: #008;">auto</span><span class=3D"styled=
-by-prettify" style=3D"color: #000;"> force_move</span><span class=3D"style=
d-by-prettify" style=3D"color: #660;">(</span><span class=3D"styled-by-pret=
tify" style=3D"color: #000;">T </span><span class=3D"styled-by-prettify" st=
yle=3D"color: #660;">&amp;</span><span class=3D"styled-by-prettify" style=
=3D"color: #000;">t</span><span class=3D"styled-by-prettify" style=3D"color=
: #660;">)</span><span class=3D"styled-by-prettify" style=3D"color: #000;">=
<br></span><span class=3D"styled-by-prettify" style=3D"color: #660;">{</spa=
n><span class=3D"styled-by-prettify" style=3D"color: #000;"><br>=C2=A0 </sp=
an><span class=3D"styled-by-prettify" style=3D"color: #008;">return</span><=
span class=3D"styled-by-prettify" style=3D"color: #000;"> T</span><span cla=
ss=3D"styled-by-prettify" style=3D"color: #660;">(</span><span class=3D"sty=
led-by-prettify" style=3D"color: #000;">std</span><span class=3D"styled-by-=
prettify" style=3D"color: #660;">::</span><span class=3D"styled-by-prettify=
" style=3D"color: #000;">move</span><span class=3D"styled-by-prettify" styl=
e=3D"color: #660;">(</span><span class=3D"styled-by-prettify" style=3D"colo=
r: #000;">t</span><span class=3D"styled-by-prettify" style=3D"color: #660;"=
>));</span><span class=3D"styled-by-prettify" style=3D"color: #000;"><br></=
span><span class=3D"styled-by-prettify" style=3D"color: #660;">}</span></di=
v></code></div><div><br></div><div>They both provoke a move. But one leaves=
 the object in the &quot;moved-from&quot; state, while the other leaves it =
in a very well defined state (assigned from a default-constructed object).<=
br></div><div><br></div><blockquote class=3D"gmail_quote" style=3D"margin: =
0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">I said=
 &quot;supplement std::move&quot;, not &quot;replace std::move&quot;. The p=
roblem is=20
<br>that the std uses the hand-waving &quot;valid, but unspecified state&qu=
ot; to=20
<br>define the moved-from state, and that forces people to call clear() or=
=20
<br>assign {} to a moved-from object to get it back into a defined state.<b=
r></blockquote><div><br></div><div>And what&#39;s wrong with that? Most of =
the time when you&#39;re moving something, you don&#39;t care what state th=
e moved-from object is in. Since more often than not, you&#39;re done with =
it.</div><div><br></div><div>The purpose of `force_move` is to ensure that =
a move actually happens. Some functions that you might pass an rvalue refer=
ence to may not actually move from it. But you may no longer want to keep t=
he object&#39;s contents around; you<i> want</i> the receiver to take it. S=
o by using `force_move`, you ensure that either the receiver moved from it =
or the temporary that was manifested will destroy the data in the object at=
 the end of that expression.</div><div><br></div><div>Either way, by the ti=
me the next statement starts, you&#39;re guaranteed that the data is gone.<=
/div><div><i></i>=C2=A0<br></div></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; 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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/ec0c2616-f0f3-42d6-9d8c-756ee532b850%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/ec0c2616-f0f3-42d6-9d8c-756ee532b850=
%40isocpp.org</a>.<br />

------=_Part_9539_566262672.1513480282239--

------=_Part_9538_1983576986.1513480282239--

.


Author: Marc Mutz <marc.mutz@kdab.com>
Date: Sun, 17 Dec 2017 14:17:48 +0100
Raw View
>> (or with CoW containers which detach in ranged for-loops *cough* Qt
>> *cough*).
>>
>> for (auto &item : std::take(m_items)) // using the shortest
>> function
>> name as a placeholder
>>
>>> In the other two cases, default initialization is
>>> not merely a default; it's a fundamental part of the algorithm.
>>>
>>> So you would need two functions: one that will move a
>>> default-constructed prvalue into the old object (and thus impose
>> both
>>> DefaultConstructible and MoveAssignable requirements on it) and
>> one
>>> which does not.
>>
>> I considered std::take (which does) and std::move (which does not)
>> to be
>> those two functions.
>
> But, as you pointed out, `std::move` does not move anything; it's just
> a cast. So `std::move` does not satisfy what I said.
>
> The two functions I'm talking about both provoke an actual,
> honest-to-God move. There's:
>
> template<typename T>
> auto move_and_clear(T &t)
> {
>   T new_t = std::move(t);
>   t = T{};
>   return new_t;
> }
>
> And there's:
>
> template<typename T>
> auto force_move(T &t)
> {
>   return T(std::move(t));
> }
>
> They both provoke a move. But one leaves the object in the
> "moved-from" state, while the other leaves it in a very well defined
> state (assigned from a default-constructed object).

Ok, I see what you mean. I'm not convinced about the need for
force_move(): Either you care about the state of the moved-from object
(that includes the case you included, where you want to ensure that any
resources it may have held are freed), then you use the new function, or
you don't, then you use std::move(). Yes, it might not actually move,
but you're not supposed to care, because not moving is a valid outcome
under the "valid, but unspecified state" rule, and something that
actually happens in practice, when the move decays to a copy (int, SSOed
string, ...).

Thanks,
Marc

--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/e95e3edc705db04d07f66674448488bc%40kdab.com.

.


Author: Richard Hodges <hodges.r@gmail.com>
Date: Sun, 17 Dec 2017 15:53:59 +0100
Raw View
--089e08200cdc69955c05608a6704
Content-Type: text/plain; charset="UTF-8"

> The two functions I'm talking about both provoke an actual,
> honest-to-God move. There's:

> template<typename T>
> auto move_and_clear(T &t)


I've been thinking along these lines myself. I'm glad you brought it up.

However the implementation for containers should preserve any
previously-allocated implementation memory so as not to cause unneccessary
news and deletes.

Here's one possible implementation, in terms of a std::clear() which
delegates to a specialised template functor to "do the right thing" for any
type.

#include <vector>
#include <cassert>


namespace std {

    // general case for anything default-constructable and moveable
    template<class T> struct specific_clear_operation
    {
        template<class Arg> void operator()(Arg&& arg) const
        {
            arg = T();
        }
    };

    // specialise for containers.. (or perhaps anything with a .clear()
method?)
    template<class T, class A> struct
specific_clear_operation<std::vector<T, A>>
    {
        template<class Arg> void operator()(Arg&& arg) const
        {
            arg.clear();
        }
    };

    template<class T> T& clear(T& arg)
    {
        auto op = specific_clear_operation<std::decay_t<T>>();
        op(arg);
        return arg;
    }

    template<class T> T move_and_clear(T& src)
    {
        auto ret = std::move(src);
        clear(src);
        return ret;
    }
}


int main()
{
    double x = 10.0;
    double y = std::move_and_clear(x);
    assert(x == 0.0);
    assert(y == 10.0);

    auto v = std::vector { 1,2,3,4,5 };
    auto w = std::move_and_clear(v);
    assert(std::size(v) == 0);
    assert(std::size(w) == 5);
}


On 17 December 2017 at 14:17, Marc Mutz <marc.mutz@kdab.com> wrote:

> (or with CoW containers which detach in ranged for-loops *cough* Qt
>>> *cough*).
>>>
>>> for (auto &item : std::take(m_items)) // using the shortest
>>> function
>>> name as a placeholder
>>>
>>> In the other two cases, default initialization is
>>>> not merely a default; it's a fundamental part of the algorithm.
>>>>
>>>> So you would need two functions: one that will move a
>>>> default-constructed prvalue into the old object (and thus impose
>>>>
>>> both
>>>
>>>> DefaultConstructible and MoveAssignable requirements on it) and
>>>>
>>> one
>>>
>>>> which does not.
>>>>
>>>
>>> I considered std::take (which does) and std::move (which does not)
>>> to be
>>> those two functions.
>>>
>>
>> But, as you pointed out, `std::move` does not move anything; it's just
>> a cast. So `std::move` does not satisfy what I said.
>>
>> The two functions I'm talking about both provoke an actual,
>> honest-to-God move. There's:
>>
>> template<typename T>
>> auto move_and_clear(T &t)
>> {
>>   T new_t = std::move(t);
>>   t = T{};
>>   return new_t;
>> }
>>
>> And there's:
>>
>> template<typename T>
>> auto force_move(T &t)
>> {
>>   return T(std::move(t));
>> }
>>
>> They both provoke a move. But one leaves the object in the
>> "moved-from" state, while the other leaves it in a very well defined
>> state (assigned from a default-constructed object).
>>
>
> Ok, I see what you mean. I'm not convinced about the need for
> force_move(): Either you care about the state of the moved-from object
> (that includes the case you included, where you want to ensure that any
> resources it may have held are freed), then you use the new function, or
> you don't, then you use std::move(). Yes, it might not actually move, but
> you're not supposed to care, because not moving is a valid outcome under
> the "valid, but unspecified state" rule, and something that actually
> happens in practice, when the move decays to a copy (int, SSOed string,
> ...).
>
> Thanks,
> Marc
>
> --
> 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.
> To view this discussion on the web visit https://groups.google.com/a/is
> ocpp.org/d/msgid/std-proposals/e95e3edc705db04d07f66674448488bc%40kdab.com
> .
>

--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CALvx3haMANEYfp3WM0JAPi7Z3FtJECGtMDwpMuX_4emsgK3rOw%40mail.gmail.com.

--089e08200cdc69955c05608a6704
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div><div>&gt; The two functions I&#39;m talking about bot=
h provoke an actual,<br>
&gt; honest-to-God move. There&#39;s:<br>
<br>
&gt; template&lt;typename T&gt;<br>
&gt; auto move_and_clear(T &amp;t)</div><div><br></div><div><br></div><div>=
I&#39;ve been thinking along these lines myself. I&#39;m glad you brought i=
t up.<br><br></div>However the implementation for containers should preserv=
e any previously-allocated implementation memory so as not to cause unnecce=
ssary news and deletes.<br><br></div>Here&#39;s one possible implementation=
, in terms of a std::clear() which delegates to a specialised template func=
tor to &quot;do the right thing&quot; for any type.<br><br><span style=3D"f=
ont-family:monospace,monospace">#include &lt;vector&gt;<br>#include &lt;cas=
sert&gt;<br><br><br>namespace std {<br><br>=C2=A0=C2=A0=C2=A0 // general ca=
se for anything default-constructable and moveable<br>=C2=A0=C2=A0=C2=A0 te=
mplate&lt;class T&gt; struct specific_clear_operation<br>=C2=A0=C2=A0=C2=A0=
 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 template&lt;class Arg&gt; =
void operator()(Arg&amp;&amp; arg) const <br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0 arg =3D T();<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=
=C2=A0=C2=A0=C2=A0 };<br><br>=C2=A0=C2=A0=C2=A0 // specialise for container=
s.. (or perhaps anything with a .clear() method?)<br>=C2=A0=C2=A0=C2=A0 tem=
plate&lt;class T, class A&gt; struct specific_clear_operation&lt;std::vecto=
r&lt;T, A&gt;&gt;<br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 template&lt;class Arg&gt; void operator()(Arg&amp;&amp; arg) c=
onst <br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 arg.clear();<br>=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0 };<br><br>=C2=A0=
=C2=A0=C2=A0 template&lt;class T&gt; T&amp; clear(T&amp; arg)<br>=C2=A0=C2=
=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 auto op =3D speci=
fic_clear_operation&lt;std::decay_t&lt;T&gt;&gt;();<br>=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0 op(arg);<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 return arg;<br>=C2=A0=C2=A0=C2=A0 }<br><br>=C2=A0=C2=A0=C2=A0 templa=
te&lt;class T&gt; T move_and_clear(T&amp; src)<br>=C2=A0=C2=A0=C2=A0 {<br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 auto ret =3D std::move(src);<br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 clear(src);<br>=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0 return ret;<br>=C2=A0=C2=A0=C2=A0 }<br>}<br><br=
><br>int main()<br>{<br>=C2=A0=C2=A0=C2=A0 double x =3D 10.0;<br>=C2=A0=C2=
=A0=C2=A0 double y =3D std::move_and_clear(x);<br>=C2=A0=C2=A0=C2=A0 assert=
(x =3D=3D 0.0);<br>=C2=A0=C2=A0=C2=A0 assert(y =3D=3D 10.0);<br><br>=C2=A0=
=C2=A0=C2=A0 auto v =3D std::vector { 1,2,3,4,5 };<br>=C2=A0=C2=A0=C2=A0 au=
to w =3D std::move_and_clear(v);<br>=C2=A0=C2=A0=C2=A0 assert(std::size(v) =
=3D=3D 0);<br>=C2=A0=C2=A0=C2=A0 assert(std::size(w) =3D=3D 5);<br>}<br></s=
pan><br><div class=3D"gmail_extra"><br><div class=3D"gmail_quote">On 17 Dec=
ember 2017 at 14:17, Marc Mutz <span dir=3D"ltr">&lt;<a href=3D"mailto:marc=
..mutz@kdab.com" target=3D"_blank">marc.mutz@kdab.com</a>&gt;</span> wrote:<=
br><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;bord=
er-left:1px solid rgb(204,204,204);padding-left:1ex"><div class=3D"gmail-HO=
EnZb"><div class=3D"gmail-h5"><blockquote class=3D"gmail_quote" style=3D"ma=
rgin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:=
1ex"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;bo=
rder-left:1px solid rgb(204,204,204);padding-left:1ex">
(or with CoW containers which detach in ranged for-loops *cough* Qt<br>
*cough*).<br>
<br>
for (auto &amp;item : std::take(m_items)) // using the shortest<br>
function<br>
name as a placeholder<br>
<br>
<blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-=
left:1px solid rgb(204,204,204);padding-left:1ex">
In the other two cases, default initialization is<br>
not merely a default; it&#39;s a fundamental part of the algorithm.<br>
<br>
So you would need two functions: one that will move a<br>
default-constructed prvalue into the old object (and thus impose<br>
</blockquote>
both<br>
<blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-=
left:1px solid rgb(204,204,204);padding-left:1ex">
DefaultConstructible and MoveAssignable requirements on it) and<br>
</blockquote>
one<br>
<blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-=
left:1px solid rgb(204,204,204);padding-left:1ex">
which does not.<br>
</blockquote>
<br>
I considered std::take (which does) and std::move (which does not)<br>
to be<br>
those two functions.<br>
</blockquote>
<br>
But, as you pointed out, `std::move` does not move anything; it&#39;s just<=
br>
a cast. So `std::move` does not satisfy what I said.<br>
<br>
The two functions I&#39;m talking about both provoke an actual,<br>
honest-to-God move. There&#39;s:<br>
<br>
template&lt;typename T&gt;<br>
auto move_and_clear(T &amp;t)<br>
{<br>
=C2=A0 T new_t =3D std::move(t);<br>
=C2=A0 t =3D T{};<br>
=C2=A0 return new_t;<br>
}<br>
<br>
And there&#39;s:<br>
<br>
template&lt;typename T&gt;<br>
auto force_move(T &amp;t)<br>
{<br>
=C2=A0 return T(std::move(t));<br>
}<br>
<br>
They both provoke a move. But one leaves the object in the<br>
&quot;moved-from&quot; state, while the other leaves it in a very well defi=
ned<br>
state (assigned from a default-constructed object).<br>
</blockquote>
<br></div></div>
Ok, I see what you mean. I&#39;m not convinced about the need for force_mov=
e(): Either you care about the state of the moved-from object (that include=
s the case you included, where you want to ensure that any resources it may=
 have held are freed), then you use the new function, or you don&#39;t, the=
n you use std::move(). Yes, it might not actually move, but you&#39;re not =
supposed to care, because not moving is a valid outcome under the &quot;val=
id, but unspecified state&quot; rule, and something that actually happens i=
n practice, when the move decays to a copy (int, SSOed string, ...).<br>
<br>
Thanks,<br>
Marc<span class=3D"gmail-"><br>
<br>
-- <br>
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals%2Bunsubscribe@isocpp.org" target=3D=
"_blank">std-proposals+unsubscribe@isoc<wbr>pp.org</a>.<br>
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org" target=3D"_blank">std-proposals@isocpp.org</a>.<br></span>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/e95e3edc705db04d07f66674448488bc%40kd=
ab.com" rel=3D"noreferrer" target=3D"_blank">https://groups.google.com/a/is=
<wbr>ocpp.org/d/msgid/std-proposals<wbr>/e95e3edc705db04d07f6667444848<wbr>=
8bc%40kdab.com</a>.<br>
</blockquote></div><br></div></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; 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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/CALvx3haMANEYfp3WM0JAPi7Z3FtJECGtMDwp=
MuX_4emsgK3rOw%40mail.gmail.com?utm_medium=3Demail&utm_source=3Dfooter">htt=
ps://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CALvx3haMANEYfp3W=
M0JAPi7Z3FtJECGtMDwpMuX_4emsgK3rOw%40mail.gmail.com</a>.<br />

--089e08200cdc69955c05608a6704--

.


Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sun, 17 Dec 2017 07:23:14 -0800 (PST)
Raw View
------=_Part_12244_847195888.1513524194287
Content-Type: multipart/alternative;
 boundary="----=_Part_12245_1690298977.1513524194287"

------=_Part_12245_1690298977.1513524194287
Content-Type: text/plain; charset="UTF-8"



On Sunday, December 17, 2017 at 8:21:31 AM UTC-5, Marc Mutz wrote:
>
> >> (or with CoW containers which detach in ranged for-loops *cough* Qt
> >> *cough*).
> >>
> >> for (auto &item : std::take(m_items)) // using the shortest
> >> function
> >> name as a placeholder
> >>
> >>> In the other two cases, default initialization is
> >>> not merely a default; it's a fundamental part of the algorithm.
> >>>
> >>> So you would need two functions: one that will move a
> >>> default-constructed prvalue into the old object (and thus impose
> >> both
> >>> DefaultConstructible and MoveAssignable requirements on it) and
> >> one
> >>> which does not.
> >>
> >> I considered std::take (which does) and std::move (which does not)
> >> to be
> >> those two functions.
> >
> > But, as you pointed out, `std::move` does not move anything; it's just
> > a cast. So `std::move` does not satisfy what I said.
> >
> > The two functions I'm talking about both provoke an actual,
> > honest-to-God move. There's:
> >
> > template<typename T>
> > auto move_and_clear(T &t)
> > {
> >   T new_t = std::move(t);
> >   t = T{};
> >   return new_t;
> > }
> >
> > And there's:
> >
> > template<typename T>
> > auto force_move(T &t)
> > {
> >   return T(std::move(t));
> > }
> >
> > They both provoke a move. But one leaves the object in the
> > "moved-from" state, while the other leaves it in a very well defined
> > state (assigned from a default-constructed object).
>
> Ok, I see what you mean. I'm not convinced about the need for
> force_move(): Either you care about the state of the moved-from object
> (that includes the case you included, where you want to ensure that any
> resources it may have held are freed), then you use the new function, or
> you don't, then you use std::move(). Yes, it might not actually move,
>
but you're not supposed to care, because not moving is a valid outcome
> under the "valid, but unspecified state" rule, and something that
> actually happens in practice, when the move decays to a copy (int, SSOed
> string, ...).
>

It's not that you care about the state of the moved-from object. It's that
you care about the state of the* data* that the possibly-moved-from object
had. You want to know that the data it stored is either in use or has been
destroyed. Leaving it in the old object is not the right choice.

Consider something like `optional::value_or`. It takes a forwarding
reference to the "or" part, which it may or may not move from. You don't
really care about the "or" object itself; what you care about is the stuff
it contains. If it allocates memory, then you want that memory to either be
in the return value or be deallocated. You don't want it to be in the old
object.

Now I admit that `value_or` is not a good example because most uses where
you're actually moving from it will likely be given a prvalue temporary
(and thus be destroyed either way at the end of the statement). But there
are other cases out there where you might move from some object or not, as
the case may be.

--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/afa2e7d4-3b7a-4146-a806-f648f6f7667c%40isocpp.org.

------=_Part_12245_1690298977.1513524194287
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br><br>On Sunday, December 17, 2017 at 8:21:31 AM UTC-5, =
Marc Mutz wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin=
-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">&gt;&gt; (or w=
ith CoW containers which detach in ranged for-loops *cough* Qt
<br>&gt;&gt; *cough*).
<br>&gt;&gt;=20
<br>&gt;&gt; for (auto &amp;item : std::take(m_items)) // using the shortes=
t
<br>&gt;&gt; function
<br>&gt;&gt; name as a placeholder
<br>&gt;&gt;=20
<br>&gt;&gt;&gt; In the other two cases, default initialization is
<br>&gt;&gt;&gt; not merely a default; it&#39;s a fundamental part of the a=
lgorithm.
<br>&gt;&gt;&gt;=20
<br>&gt;&gt;&gt; So you would need two functions: one that will move a
<br>&gt;&gt;&gt; default-constructed prvalue into the old object (and thus =
impose
<br>&gt;&gt; both
<br>&gt;&gt;&gt; DefaultConstructible and MoveAssignable requirements on it=
) and
<br>&gt;&gt; one
<br>&gt;&gt;&gt; which does not.
<br>&gt;&gt;=20
<br>&gt;&gt; I considered std::take (which does) and std::move (which does =
not)
<br>&gt;&gt; to be
<br>&gt;&gt; those two functions.
<br>&gt;=20
<br>&gt; But, as you pointed out, `std::move` does not move anything; it&#3=
9;s just
<br>&gt; a cast. So `std::move` does not satisfy what I said.
<br>&gt;=20
<br>&gt; The two functions I&#39;m talking about both provoke an actual,
<br>&gt; honest-to-God move. There&#39;s:
<br>&gt;=20
<br>&gt; template&lt;typename T&gt;
<br>&gt; auto move_and_clear(T &amp;t)
<br>&gt; {
<br>&gt; =C2=A0 T new_t =3D std::move(t);
<br>&gt; =C2=A0 t =3D T{};
<br>&gt; =C2=A0 return new_t;
<br>&gt; }
<br>&gt;=20
<br>&gt; And there&#39;s:
<br>&gt;=20
<br>&gt; template&lt;typename T&gt;
<br>&gt; auto force_move(T &amp;t)
<br>&gt; {
<br>&gt; =C2=A0 return T(std::move(t));
<br>&gt; }
<br>&gt;=20
<br>&gt; They both provoke a move. But one leaves the object in the
<br>&gt; &quot;moved-from&quot; state, while the other leaves it in a very =
well defined
<br>&gt; state (assigned from a default-constructed object).
<br>
<br>Ok, I see what you mean. I&#39;m not convinced about the need for=20
<br>force_move(): Either you care about the state of the moved-from object=
=20
<br>(that includes the case you included, where you want to ensure that any=
=20
<br>resources it may have held are freed), then you use the new function, o=
r=20
<br>you don&#39;t, then you use std::move(). Yes, it might not actually mov=
e,=20
<br></blockquote><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">but you&#39;r=
e not supposed to care, because not moving is a valid outcome=20
<br>under the &quot;valid, but unspecified state&quot; rule, and something =
that=20
<br>actually happens in practice, when the move decays to a copy (int, SSOe=
d=20
<br>string, ...).
<br></blockquote><div><br></div><div>It&#39;s not that you care about the s=
tate of the moved-from object. It&#39;s that you care about the state of th=
e<i> data</i> that the possibly-moved-from object had. You want to know tha=
t the data it stored is either in use or has been destroyed. Leaving it in =
the old object is not the right choice.</div><div><br></div><div>Consider s=
omething like `optional::value_or`. It takes a forwarding reference to the =
&quot;or&quot; part, which it may or may not move from. You don&#39;t reall=
y care about the &quot;or&quot; object itself; what you care about is the s=
tuff it contains. If it allocates memory, then you want that memory to eith=
er be in the return value or be deallocated. You don&#39;t want it to be in=
 the old object.</div><div><br></div><div>Now I admit that `value_or` is no=
t a good example because most uses where you&#39;re actually moving from it=
 will likely be given a prvalue temporary (and thus be destroyed either way=
 at the end of the statement). But there are other cases out there where yo=
u might move from some object or not, as the case may be.<br></div></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; 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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/afa2e7d4-3b7a-4146-a806-f648f6f7667c%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/afa2e7d4-3b7a-4146-a806-f648f6f7667c=
%40isocpp.org</a>.<br />

------=_Part_12245_1690298977.1513524194287--

------=_Part_12244_847195888.1513524194287--

.


Author: Nicol Bolas <jmckesson@gmail.com>
Date: Sun, 17 Dec 2017 07:26:28 -0800 (PST)
Raw View
------=_Part_12427_931222973.1513524388529
Content-Type: multipart/alternative;
 boundary="----=_Part_12428_1825467434.1513524388529"

------=_Part_12428_1825467434.1513524388529
Content-Type: text/plain; charset="UTF-8"



On Sunday, December 17, 2017 at 9:54:02 AM UTC-5, Richard Hodges wrote:
>
> > The two functions I'm talking about both provoke an actual,
> > honest-to-God move. There's:
>
> > template<typename T>
> > auto move_and_clear(T &t)
>
>
> I've been thinking along these lines myself. I'm glad you brought it up.
>
> However the implementation for containers should preserve any
> previously-allocated implementation memory so as not to cause unneccessary
> news and deletes.
>

If you move from a container, any "previously-allocated implementation
memory" should have been transferred to the new object, right? So what
"news and deletes" would we be talking about?

--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/4193af37-69cd-42a0-9903-ccdcbd17aa28%40isocpp.org.

------=_Part_12428_1825467434.1513524388529
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br><br>On Sunday, December 17, 2017 at 9:54:02 AM UTC-5, =
Richard Hodges wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;m=
argin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=
=3D"ltr"><div><div>&gt; The two functions I&#39;m talking about both provok=
e an actual,<br>
&gt; honest-to-God move. There&#39;s:<br>
<br>
&gt; template&lt;typename T&gt;<br>
&gt; auto move_and_clear(T &amp;t)</div><div><br></div><div><br></div><div>=
I&#39;ve been thinking along these lines myself. I&#39;m glad you brought i=
t up.<br><br></div>However the implementation for containers should preserv=
e any previously-allocated implementation memory so as not to cause unnecce=
ssary news and deletes.<br></div></div></blockquote><div><br></div><div>If =
you move from a container, any &quot;<span style=3D"display: inline !import=
ant; float: none; background-color: transparent; color: rgb(34, 34, 34); fo=
nt-family: &quot;Arial&quot;,&quot;Helvetica&quot;,sans-serif; font-size: 1=
3px; font-style: normal; font-variant: normal; font-weight: 400; letter-spa=
cing: normal; orphans: 2; text-align: left; text-decoration: none; text-ind=
ent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space=
: normal; word-spacing: 0px;">previously-allocated implementation memory&qu=
ot; should have been transferred to the new object, right? So what &quot;ne=
ws and deletes&quot; would we be talking about?</span></div><div><br></div>=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;">
</blockquote></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; 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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/4193af37-69cd-42a0-9903-ccdcbd17aa28%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/4193af37-69cd-42a0-9903-ccdcbd17aa28=
%40isocpp.org</a>.<br />

------=_Part_12428_1825467434.1513524388529--

------=_Part_12427_931222973.1513524388529--

.