[move] one more dumb question

how do i make a function return an object by value and other object to, say, move assign from that returned object within current C++ std? i.e. my_type foo() {/*...*/} //... my_type a; a = foo(); //move assignment is intended here -- Pavel

DE wrote:
how do i make a function return an object by value and other object to, say, move assign from that returned object within current C++ std? i.e.
my_type foo() {/*...*/} //... my_type a; a = foo(); //move assignment is intended here
-- Pavel
Fortunately, you don't have to worry about the return type of foo; just return by value, as you normally would. Return Value Optimization (RVO) (or whatever mechanism you want to attribute this to) takes care of eliding the copy between the "return" in foo and the call site. So now you just need to worry about which operator= the assignment will call. Currently, I believe, if you use the move-enabling macros in Boost.Move, then the move assignment operator of my_type will be chosen for the assignment operation, rather than the copy assignment operator. The "trick" that Boost.Move employs is to declare the copy assignment operators as operator=(T&) and operator=(const rv<T>&); and the move assignment operator as operator=(rv<T>&). The end result is that rvalues of type T bind to the move assignment operator, while all lvalues will bind to one of the copy assignment operators. Thus, the above assignment, "a = foo()", will move assign. There has been some discussion, however, that this mechanism has undesirable side effects, one of which is the "poisoning" of the auto-generated copy assignment operator for all classes that enclose a class (inheritance or member object) that use a move-enabling macro. The auto-generated copy assignment operator will take its parameter by reference to non-const, which is Bad. The alternative is, for move-enabled classes, to write a single assignment operator accepting its parameter by value. This is safe, specifically addressing the above "poisoning" problem, if non-optimal for classes with member objects for which copying and moving is identical. I've actually experimented with different alternatives to the move-enabling macros (as well as how to implement forwarding), and at some point soon will present my thoughts to Ion on what I think could be done. - Jeff

Fortunately, you don't have to worry about the return type of foo; just return by value, as you normally would. Return Value Optimization (RVO) (or whatever mechanism you want to attribute this to) takes care of eliding the copy between the "return" in foo and the call site. So now you just need to worry about which operator= the assignment will call.
on 26.02.2010 at 0:20 Jeffrey Hellrung wrote : this' clear
Currently, I believe, if you use the move-enabling macros in Boost.Move, then the move assignment operator of my_type will be chosen for the assignment operation, rather than the copy assignment operator. The "trick" that Boost.Move employs is to declare the copy assignment operators as operator=(T&) and operator=(const rv<T>&); and the move assignment operator as operator=(rv<T>&). The end result is that rvalues of type T bind to the move assignment operator, while all lvalues will bind to one of the copy assignment operators. Thus, the above assignment, "a = foo()", will move assign. i can't fully understand this so far on one hand there can be functions returning 'type' or 'const type' how current implementation interact with it? on the other hand there is a (widespread?) relaxation of the standard according to which a reference can be bound to an lvalue (at least msvc80 provide it) does the implementation handle it? i'm not talking about non-canonical form of the copy assignment (i mean 'operator=(const t&)')...
There has been some discussion, however, that this mechanism has undesirable side effects, one of which is the "poisoning" of the auto-generated copy assignment operator for all classes that enclose a class (inheritance or member object) that use a move-enabling macro. The auto-generated copy assignment operator will take its parameter by reference to non-const, which is Bad. indeed, but personally i don't consider this a big issue
The alternative is, for move-enabled classes, to write a single assignment operator accepting its parameter by value. This is safe, specifically addressing the above "poisoning" problem, if non-optimal for classes with member objects for which copying and moving is identical.
I've actually experimented with different alternatives to the move-enabling macros (as well as how to implement forwarding), and at some point soon will present my thoughts to Ion on what I think could be done. i bet you tried simpler solutions then why not to choose a simpler one?
- Jeff _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Pavel

DE wrote:
Currently, I believe, if you use the move-enabling macros in Boost.Move, then the move assignment operator of my_type will be chosen for the assignment operation, rather than the copy assignment operator. The "trick" that Boost.Move employs is to declare the copy assignment operators as operator=(T&) and operator=(const rv<T>&); and the move assignment operator as operator=(rv<T>&). The end result is that rvalues of type T bind to the move assignment operator, while all lvalues will bind to one of the copy assignment operators. Thus, the above assignment, "a = foo()", will move assign. i can't fully understand this so far on one hand there can be functions returning 'type' or 'const type' how current implementation interact with it? on the other hand there is a (widespread?) relaxation of the standard according to which a reference can be bound to an lvalue (at least msvc80 provide it) does the implementation handle it? i'm not talking about non-canonical form of the copy assignment (i mean 'operator=(const t&)')...
Yes, the type of an rvalue can be const-qualified, but the current mechanism cannot distinguish between a const-qualified rvalue and a const-qualified lvalue. So only returning by non-const value will work. For a simple demonstration of the basic mechanism (untested, so not sure if it will compile, but it should give the basic idea) #include <iostream> template< class T > struct rv : T { }; struct movable { operator rv< movable >& () { return static_cast< rv< movable >&
(*this); } operator const rv< movable >& () const { return static_cast< const rv< movable >& >(*this); } };
movable rvalue() { return movable(); } const movable const_rvalue() { return movable(); } void foo(movable&) { std::cout << "foo(movable&)" << std::endl; } void foo(const rv< movable >&) { std::cout << "foo(const rv< movable
&)" << std::endl; } void foo(rv< movable >&) { std::cout << "foo(rv< movable >&)" << std::endl; }
int main(int argc, char* argv[]) { movable lvalue; const movable const_lvalue; foo(lvalue); foo(const_lvalue); foo(rvalue()); foo(const_rvalue()); return 0; } Output should be "foo(movable&)" "foo(const rv< movable >&)" "foo(rv< movable >&)" "foo(const rv< movable >&)"
There has been some discussion, however, that this mechanism has undesirable side effects, one of which is the "poisoning" of the auto-generated copy assignment operator for all classes that enclose a class (inheritance or member object) that use a move-enabling macro. The auto-generated copy assignment operator will take its parameter by reference to non-const, which is Bad. indeed, but personally i don't consider this a big issue
It is a big issue when mixing move-enabled classes with pre-move-emulation classes (e.g., using a std::pair< movable, movable >) within C++03.
I've actually experimented with different alternatives to the move-enabling macros (as well as how to implement forwarding), and at some point soon will present my thoughts to Ion on what I think could be done. i bet you tried simpler solutions then why not to choose a simpler one?
I don't have any simpler solutions, only a spectrum of solutions to select the best set of tradeoffs for a given situation. Which, in some sense, is more complicated. - Jeff

on 26.02.2010 at 21:27 Jeffrey Hellrung wrote :
Yes, the type of an rvalue can be const-qualified, but the current mechanism cannot distinguish between a const-qualified rvalue and a const-qualified lvalue. So only returning by non-const value will work.
For a simple demonstration of the basic mechanism (untested, so not sure if it will compile, but it should give the basic idea) [code here] oh now i get it! i didn't realize there are operator rv<T>& and its const counterpart hidden behind the macros thanks for your effort explaining this
I don't have any simpler solutions, only a spectrum of solutions to select the best set of tradeoffs for a given situation. Which, in some sense, is more complicated. so summing it all up i say that this solution is far too tricky to be useful no offence it seems to me too restrictive and too incomplete the infrastructure is too complicated on the other hand i guess forcing users to return something like rv<t> from functions like this
rv<my_type> foo(); is not an option another question arises: who is the target audience (aka users) of this lib? -- Pavel

On 26 Feb 2010, at 20:11, DE wrote:
on 26.02.2010 at 21:27 Jeffrey Hellrung wrote :
Yes, the type of an rvalue can be const-qualified, but the current mechanism cannot distinguish between a const-qualified rvalue and a const-qualified lvalue. So only returning by non-const value will work.
For a simple demonstration of the basic mechanism (untested, so not sure if it will compile, but it should give the basic idea) [code here] oh now i get it! i didn't realize there are operator rv<T>& and its const counterpart hidden behind the macros thanks for your effort explaining this
I don't have any simpler solutions, only a spectrum of solutions to select the best set of tradeoffs for a given situation. Which, in some sense, is more complicated. so summing it all up i say that this solution is far too tricky to be useful no offence it seems to me too restrictive and too incomplete the infrastructure is too complicated on the other hand i guess forcing users to return something like rv<t> from functions like this
rv<my_type> foo();
is not an option
another question arises: who is the target audience (aka users) of this lib?
I use this lib myself, as it implements a significant subset of "true rvalue references" from C++0x. Using this library, and some care, we can write optimal code for new C++0x compilers, while maintaining support for older C++03 compilers as well. Chris

DE wrote:
For a simple demonstration of the basic mechanism (untested, so not sure if it will compile, but it should give the basic idea) [code here] oh now i get it! i didn't realize there are operator rv<T>& and its const counterpart hidden behind the macros
on 26.02.2010 at 21:27 Jeffrey Hellrung wrote : <snip> thanks for your effort explaining this
No problem. I have suggested to Ion that a section of the Boost.Move documentation should be devoted to explaining how the mechanism works. <snip>
so summing it all up i say that this solution is far too tricky to be useful
I think the same could be said for a lot of "tricks" Boost libraries employ ;) I'd have to put Boost.EnableIf and Boost.Preprocessor in the category of code that uses language features in non-obvious ways.
no offence it seems to me too restrictive and too incomplete the infrastructure is too complicated
Regarding "too restrictive and too incomplete", you still can't get perfect forwarding. But otherwise, you pretty much get identical behavior as with C++0x rvalue references. So I would disagree with this claim. Regarding "too complicated", the basic mechanism (and most of the nastiness) can be encapsulated behind a macro, and as long as you adopt certain conventions, writing move-aware code otherwise is very easy. From the user's point-of-view, there is some complication and extra consideration, but not really any more than you'd have with true rvalue references. However, everyone's entitled to their own opinion ;) <snip>
another question arises: who is the target audience (aka users) of this lib?
I use it, and I think Chris stated the motivation for using it very well. I believe it can be targeted to anyone wishing to write move-aware code. - Jeff

on 26.02.2010 at 23:34 Jeffrey Hellrung wrote :
[many clever words here]
another question arises: who is the target audience (aka users) of this lib?
I use it, and I think Chris stated the motivation for using it very well. I believe it can be targeted to anyone wishing to write move-aware code. i agree with both you and christopher in the end you convinced me
thinking a while i figured out that in fact the lib as it is now is pretty useful to a certain degree but there should be provided some kind of cautions/warnings outlining drawbacks of the current implementation it should say to a novice programmer that it must be used with great care or, in other words, just like it is said in the docs at the same time an advanced programmer should be able to handle all the minutae as he prefer all these is imho of course -- Pavel
participants (3)
-
Christopher Jefferson
-
DE
-
Jeffrey Hellrung