C++ Logo


Advanced search

Re: std::take(obj), aka std::exchange(obj, {})

From: Giuseppe D'Angelo <giuseppe.dangelo_at_[hidden]>
Date: Fri, 25 Sep 2020 01:56:09 +0200

Il 25/09/20 00:15, Arthur O'Dwyer ha scritto:
> I don't understand how this is different from
> newobj = std::exchange(obj, {});
> Even after reading the motivation... is it just to save a few characters?
> newobj = std::exchange(obj, {}); // longer
> newobj = std::take(obj); // shorter

At face value, "yes", given the super-qualified majority of usages :)

At deep value, it's introducing a new verb for this idiom.

> The big costs of `std::exchange(obj, {})` as far as I'm concerned are
> that you have to default-construct a whole T object, pass std::exchange
> a /reference/ to that object, call the assignment operator which has to
> conditionally free the old resource, and finally destroy the temporary T
> object (which has to conditionally free the resource). I can see how it
> would be beneficial to coalesce some of these operations into a tighter
> package like std::take. But I don't see how your suggested
> implementation of std::take actually helps. You're still constructing a
> temporary T, calling the assignment operator, and calling the destructor
> of the temporary.
> // std::take of a string
> {
> std::string temp{};
> newobj = std::move(obj);
> obj = std::move(temp);
> } // temp.~string();
> // what you want instead
> newobj = std::move(obj);
> obj.clear();

I don't think these two are equivalent.

The second doesn't work for an arbitrary obj whose moved-from state is
invalid -- meaning you can't call clear() on it. take() works in all cases.

Limiting ourselves to types for which it would work (e.g. std::string,
under the valid but unspecified contract of the standard library), then
the _observable_ post conditions are not the same.

With something like

   new_string = std::move(old_string);

then old_string's state is not guaranteed to be in the std::string's
default constructed state. In all likelihood, old_string will have
leftover capacity: namely, *new_string*'s capacity before the move
assignment (assuming that it's done via pure swap, not via move and
swap). Also, in all likelihood, old_string's size is 0 right after the
move, so clear() here is a no op.

If you're looking exactly for these post conditions then there's
probably another idiom one can identify here ("recycle storage idiom"?),
possibly implementable via swap + clear. But this idiom is not take().

> Here's the difference in codegen: https://godbolt.org/z/n54arb
> So the $64,000 question is: How do you get the good codegen?

I can't make apples and oranges' codegen be equal...

For what it's worth, take(obj) produces slightly better code than
exchange(obj, {}):

> https://godbolt.org/z/EGr39j

And if it's used for move construction rather than for move assignment,
then it's actually just like the proposed move (+clear()):

> https://godbolt.org/z/nxrvo8

> If you can't get the good codegen, then I don't see the point of making
> a wrapper around std::exchange(obj, {}). It's already short and clear
> enough — certainly clearer than std::take(obj), /this year/, since
> people have already had 20 years to learn what std::exchange means.

Sorry, I don't buy the "20 years" claim. Surely the idea is at least
that old and exchange-like asm instructions have existed for a very long

But the usable tool for the C++ programmer only came in C++14 (and in
Boost / ABSL even later than that!). Hence I'm not really surprised that
it's somehow "rarely" used, not regularly taught, not fully discovered, etc.

> You'll have to teach them what std::take means, and (more importantly)
> why to use it. If you can't explain why to use it, then it's not a
> good idea. "It produces better codegen" would be a great explanation...
> but how do we get from here to there?

I am addressing some of the "why to use it" in the proposal itself. Also
if SG20 (a target audience) has inputs I'll be glad to discuss them.

Thank you,
Giuseppe D'Angelo | giuseppe.dangelo_at_[hidden] | Senior Software Engineer
KDAB (France) S.A.S., a KDAB Group company
Tel. France +33 (0)4 90 84 08 53, http://www.kdab.com
KDAB - The Qt, C++ and OpenGL Experts

Received on 2020-09-24 18:56:17