Subject: Re: [boost] [move] interest: the pass-by-value and swap idiom, and explicit copy constructors
From: Krzysztof Czainski (1czajnik_at_[hidden])
Date: 2014-02-15 04:54:08


2014-02-15 1:13 GMT+01:00 Adam Wulkiewicz <adam.wulkiewicz_at_[hidden]>:

> Hi,
>
>
> Krzysztof Czainski wrote:
>
>> Hello,
>>
>> I would like to ask about interest in an idea of mine. I described it in
>> detail [1] and implemented it as part of my cz libraries [2].
>>
>> In short, the idea presents:
>> - an alternative to implicitly copyable and movable types: making them
>> explicitly copyable;
>> - a protocol for explicitly stating in code, where a copy is to be made
>> (boost::copy()), which compliments the protocol for explicitly stating in
>> code, where a move is allowed (std::move() or boost::move());
>> - a Boost.move extension for implementing movable types with use of the
>> pass-by-value and swap idiom, backwards-compatible with C++98, which
>> allows
>> to choose copying policies (and eases changing between these policies
>> during maintenance):
>> - movable but not copyable,
>> - movable and explicitly-copyable,
>> - movable and copyable;
>> - an example, showing how simply a CloneablePtr can thereby be
>> implemented.
>>
>> This extension to Boost.move is hosted and developed as part of my cz
>> libraries [2], but it isn't tied to them at all. It only consists of two
>> headers. I extracted the two headers together with samples and docs, and
>> they are available as a zip archive [3].
>>
>> I would be most excited if there was interest in adding this functionality
>> to Boost.move, but in any case, all comments are much appreciated.
>>
>> Regards,
>> Kris
>>
>> [1] http://kristoczaj.bitbucket.org/cz/copy_move_types.html
>> [2] http://kristoczaj.bitbucket.org/cz/index.html
>> [3] http://bitbucket.org/kristoczaj/cz/downloads/move-0.7.1.zip
>>
>>
> Your proposal reminds me Scott Meyers' lecture (http://channel9.msdn.com/
> Events/GoingNative/2013/An-Effective-Cpp11-14-Sampler):
> "std::copy() doesn't copy and std::forward() doesn't forward"
>

I haven't seen this yet, thank you for this reference.

> your boost::copy() is different because it indeed copies something ;).
>
> I have a few questions.
>
> 1. I think that the results of the first example with Matrices (implicitly
> declared ctors and assignments) are wrong. What compiler do you use? I've
> tested it in GCC 4.7 and Clang 3.2 with -std=c++11.
>

I just tested it with MinGW-4.8, and I must say you are right, Adam --
thank you for pointing this out. I must have added the const in the first
line of main(), and missed the fact, that output had changed - I apologize.
The output I get with the const in there is:

C++11 | C++98
-------------------- | --------------------
CopyMoveDemo() | CopyMoveDemo()
CopyMoveDemo(const&) | CopyMoveDemo(const&)
CopyMoveDemo(const&) | CopyMoveDemo(const&)
### | ###
CopyMoveDemo(const&) | CopyMoveDemo(const&)
swap CopyMoveDemo | swap CopyMoveDemo
~CopyMoveDemo() | ~CopyMoveDemo()
CopyMoveDemo(&&) | CopyMoveDemo(__const&__)
swap CopyMoveDemo | swap CopyMoveDemo
~CopyMoveDemo() | ~CopyMoveDemo()
### | ###
~CopyMoveDemo() | ~CopyMoveDemo()
~CopyMoveDemo() | ~CopyMoveDemo()
~CopyMoveDemo() | ~CopyMoveDemo()

But without the const, i.e. Matrix a; in the first line of main(), the
output is as presented in the paper. It makes sense, because a move() on a
const object returns Matrix const&&, which simply chooses the copy
constructor as the best overload in this case.

a) can't move from a const object, this is a copy
> Matrix const a;
> Matrix c = boost::move(a);
>
Right. So the correct example should be without the const - sorry.

I've corrected this in the article. Should I maintain some change log?

b) here I have a copy as well, probably because Matrix has a non-static
> data member without a move assignment operator and that is is not trivially
> copyable
> c = boost::move(b);
> after declaring "proper" move assignment in the member class (instead of
> copy and swap) or using copy and swap + move ctor in Matrix everything
> works as expected.
>

Do you get a copy in C++11 here? I get a move() in C++11, and a copy in
C++98. And that's what I expect, because C++11 auto-generates copy- and
move-assignment operators, and both use the pass-by-value assignment of
CopyMoveDemo correctly.

> Assuming that my understanding is correct, with copy and swap the move
> assignment operator can't be implicitly declared.

Why?

> So you'll be forced to declare it manually even in C++11.

I don't think so. I think, the auto-generated copy- and move-assignments
work with it correctly.

AFAIK the currently used move emulation doesn't have this limitation. Or am
> I missing something?
>
In C++98 only a copy-assignment operator is auto-generated, so the class
just isn't movable.

>
> 2. About the example with Matrices addition and moves, the one with e.g.
> this line:
>
> Matrix e = boost::move(b) + c + d;
>
> Is it just an example of using boost::move() needed by the next
> complimentary example of using boost::copy(), showing that you could do
> something like this?

Yes, this is just to show, that we can make this compile. It only makes
sense if we no longer need `b` in this function, which may sometimes be the
case.

> Or do you think that this is the right way of implementing e.g. the
> addition to avoid temporary copies? I'm asking because it's a little too
> clever on my taste. IMHO the way with the 4 overloads is more intuitive.

I think, the 4 overload are kind of orthogonal to my explicit-copy
proposal. I am proposing to make all copies explicit. And the purpose of
the 4 overloads is so that more implicit copies can be avoided. One could
combine both approaches. That would mean removing one of the 4 overloads,
which makes an implicit copy, and taking advantage of the 3 remaining,
while still requiring the copy to be explicit.

> On some review it could certainly increase the value of WTF/minute ;) (
> http://www.osnews.com/story/19266/WTFs_m).

I take your point. But still, sometimes I'd rather see pointed out in code,
where copies are happening.

Regards,
Kris