Boost logo

Boost :

Subject: Re: [boost] [move] move assignment from lvalue returned by func (was: one more dumb question)
From: Jeffrey Hellrung (jhellrung_at_[hidden])
Date: 2010-02-26 13:27:19


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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk