[move] interest: the pass-by-value and swap idiom, and explicit copy constructors

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

On Fri, Feb 14, 2014 at 05:52:53PM +0100, Krzysztof Czainski wrote:
- a protocol for explicitly stating in code, where a copy is to be made (boost::copy()), which compliments the protocol for explicitly stating in
Note that boost::copy already exists as the widely used Range-based complement of std::copy algorithm. I just want to get this out of the way so people can focus on the actual proposal and not parrot this over and over again.
code, where a move is allowed (std::move() or boost::move());
-- Lars Viklund | zao@acc.umu.se

2014-02-14 18:16 GMT+01:00 Lars Viklund
On Fri, Feb 14, 2014 at 05:52:53PM +0100, Krzysztof Czainski wrote:
- a protocol for explicitly stating in code, where a copy is to be made (boost::copy()), which compliments the protocol for explicitly stating in
Note that boost::copy already exists as the widely used Range-based complement of std::copy algorithm.
I just want to get this out of the way so people can focus on the actual proposal and not parrot this over and over again.
Good point, thanks Lars. Note, that an algorithm boost::move also exists: http://www.boost.org/doc/libs/1_55_0/doc/html/move/move_algorithms.html And I'm not sure how a Range-based complement to the move algorithm could look like, but it may also turn out useful. Regards, Kris

Hi, Krzysztof Czainski wrote:
2014-02-14 18:16 GMT+01:00 Lars Viklund
: On Fri, Feb 14, 2014 at 05:52:53PM +0100, Krzysztof Czainski wrote:
- a protocol for explicitly stating in code, where a copy is to be made (boost::copy()), which compliments the protocol for explicitly stating in Note that boost::copy already exists as the widely used Range-based complement of std::copy algorithm.
I just want to get this out of the way so people can focus on the actual proposal and not parrot this over and over again.
Good point, thanks Lars.
Note, that an algorithm boost::move also exists: http://www.boost.org/doc/libs/1_55_0/doc/html/move/move_algorithms.html And I'm not sure how a Range-based complement to the move algorithm could look like, but it may also turn out useful.
Probably like this: boost::move(range, out_it) ; Regards, Adam

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-Sampl...): "std::copy() doesn't copy and std::forward() doesn't forward" 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. a) can't move from a const object, this is a copy Matrix const a; Matrix c = boost::move(a); 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. Assuming that my understanding is correct, with copy and swap the move assignment operator can't be implicitly declared. So you'll be forced to declare it manually even in C++11. AFAIK the currently used move emulation doesn't have this limitation. Or am I missing something? 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? 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. On some review it could certainly increase the value of WTF/minute ;) (http://www.osnews.com/story/19266/WTFs_m). Regards, Adam

2014-02-15 1:13 GMT+01:00 Adam Wulkiewicz
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

Hi, Krzysztof Czainski wrote:
2014-02-15 1:13 GMT+01:00 Adam Wulkiewicz
: 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.
Yes, in GCC4.7, Clang3.2, MinGW 4.7, VS2010, VS2013. However VS isn't good for testing the standard. For instance it doesn't implicitly delete the copy ctor if move ctor is defined and therefore permits copying.
Assuming that my understanding is correct, with copy and swap the move assignment operator can't be implicitly declared. Why?
I'll leave the interpretation of the standard to smarter than me but according to this: http://en.cppreference.com/w/cpp/language/move_operator <cite> The implicitly-declared or defaulted move assignment operator for class |T| is defined as /deleted/ in any of the following is true: ... |- T| has a non-static data member or a direct or virtual base without a move assignment operator that is not trivially copyable. </cite> Our member isn't trivially copyable and hasn't declared move assignment, because our assignment takes parameter by value not by && so it's not move assignment. Well, not the explicitly defined one, according to the same page: <cite> A move assignment operator of class |T| is a non-template non-static member function with the name operator= that takes exactly one parameter of type T&&, const T&&, volatile T&&, or const volatile T&&. </cite> Btw, according to page: http://en.cppreference.com/w/cpp/language/as_operator <cite> A copy assignment operator of class |T| is a non-template non-static member function with the name operator= that takes exactly one parameter of type T, T&, const T&, volatile T&, or const volatile T& </cite> so our assignment operator is copy assignment. So is there an implicitly declared move assignment in the member class? According to the first mentioned page: <cite> If no user-defined move assignment operators are provided for a class type (struct, class, or union), and all of the following is true: * there are no user-declared copy constructors * there are no user-declared move constructors * there are no user-declared copy assignment operators * there are no user-declared destructors * the implicitly-declared move assignment operator would not be defined as deleted </cite> AFAIU only the last point is true. So this is my reasoning. I'd say that there is a bug in GCC4.8, but maybe it's a feature ;) Btw, in the same time std::is_move_assignable<Matrix>::value == true (MinGW 4.7) Regards, Adam

2014-02-15 14:11 GMT+01:00 Adam Wulkiewicz
Hi,
Krzysztof Czainski wrote:
2014-02-15 1:13 GMT+01:00 Adam Wulkiewicz
: 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.
Yes, in GCC4.7, Clang3.2, MinGW 4.7, VS2010, VS2013.
However VS isn't good for testing the standard. For instance it doesn't implicitly delete the copy ctor if move ctor is defined and therefore permits copying.
Assuming that my understanding is correct, with copy and swap the move
assignment operator can't be implicitly declared.
Why?
I'll leave the interpretation of the standard to smarter than me but according to this: http://en.cppreference.com/w/cpp/language/move_operator
<cite> The implicitly-declared or defaulted move assignment operator for class |T| is defined as /deleted/ in any of the following is true: ... |- T| has a non-static data member or a direct or virtual base without a move assignment operator that is not trivially copyable. </cite>
Our member isn't trivially copyable and hasn't declared move assignment, because our assignment takes parameter by value not by && so it's not move assignment. Well, not the explicitly defined one, according to the same page:
<cite> A move assignment operator of class |T| is a non-template non-static member function with the name operator= that takes exactly one parameter of type T&&, const T&&, volatile T&&, or const volatile T&&. </cite>
Btw, according to page: http://en.cppreference.com/w/ cpp/language/as_operator
<cite> A copy assignment operator of class |T| is a non-template non-static member function with the name operator= that takes exactly one parameter of type T, T&, const T&, volatile T&, or const volatile T& </cite>
so our assignment operator is copy assignment.
Hmm, it's a pity IMHO, but I agree. So if class X has a base or member Y with an assignment operator taking Y by value, it implies X's auto-generated move assignment is declared deleted. Can anyone please confirm these requirements with the standard?
So is there an implicitly declared move assignment in the member class? According to the first mentioned page:
<cite> If no user-defined move assignment operators are provided for a class type (struct, class, or union), and all of the following is true:
* there are no user-declared copy constructors * there are no user-declared move constructors * there are no user-declared copy assignment operators * there are no user-declared destructors * the implicitly-declared move assignment operator would not be defined as deleted
</cite>
AFAIU only the last point is true.
So this is my reasoning. I'd say that there is a bug in GCC4.8, but maybe it's a feature ;)
Btw, in the same time std::is_move_assignable<Matrix>::value == true (MinGW 4.7)
Interesting, thanks for your input, Adam. Regards, Kris

2014-02-15 14:58 GMT+01:00 Krzysztof Czainski <1czajnik@gmail.com>:
2014-02-15 14:11 GMT+01:00 Adam Wulkiewicz
: Hi,
Krzysztof Czainski wrote:
2014-02-15 1:13 GMT+01:00 Adam Wulkiewicz
: 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.
Yes, in GCC4.7, Clang3.2, MinGW 4.7, VS2010, VS2013.
However VS isn't good for testing the standard. For instance it doesn't implicitly delete the copy ctor if move ctor is defined and therefore permits copying.
Assuming that my understanding is correct, with copy and swap the move
assignment operator can't be implicitly declared.
Why?
I asked about this on comp.lang.c++.moderated: https://groups.google.com/forum/#!topic/comp.lang.c++.moderated/6KvOCkFdcR8
In short, MinGW-4.8 seems to follow the proposed resolution to this defect: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1402 In any case, don't let this issue hide the main idea in my proposal, which is: - adding to Boost.move a possibility for the user to define the assignment operator in terms of pass-by-value and swap; - making copy constructors explicit, to be used together with `copy()` -- a complimentary function to `move()`. Cheers, Kris

On 2/14/2014 10:52 AM, Krzysztof Czainski wrote:
(boost::copy()), which compliments the protocol for explicitly stating in code, where a move is allowed (std::move() or boost::move());
My recent experience is that the choice of std::move or boost::move depends on the destination, not the object itself. My situation is when Boost is built with move emulation but the compiler really does have rvalue references. If I'm calling something that is built with move emulation only (like boost containers) then it is necessary to use boost::move around the parameter. For my own functions which use &&, then std::move (or my work-alike in my own namespace, for C++03 std libraries) is required. I was thinking originally of having my move work-alike figure out if it's a move-emulation-enabled type and doing it the boost way, or not and doing it the native way. But I quickly discovered that the same class needs both applied to it in different situations! I'm especially interested in having boost.Move support the situation where it is built with NO_RVALUE_REFERENCES but they really are available, but the standard library doesn't have std::move, forward, etc.

El 20/02/2014 22:37, John M. Dlugosz escribió:
On 2/14/2014 10:52 AM, Krzysztof Czainski wrote:
(boost::copy()), which compliments the protocol for explicitly stating in code, where a move is allowed (std::move() or boost::move());
My recent experience is that the choice of std::move or boost::move depends on the destination, not the object itself. My situation is when Boost is built with move emulation but the compiler really does have rvalue references.
If the compiler supports rvalue references how is Boost.Move built with move emulation? Why is this situation useful for you? Ion
participants (5)
-
Adam Wulkiewicz
-
Ion Gaztañaga
-
John M. Dlugosz
-
Krzysztof Czainski
-
Lars Viklund