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