
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