Boost logo

Boost :

Subject: Re: [boost] [Review] Formal Review: Boost.Move
From: Thomas Klimpel (Thomas.Klimpel_at_[hidden])
Date: 2010-09-19 12:43:17


Dave Abrahams wrote:
> Thomas Klimpel wrote:
> > self_t& operator=(self_t&& rr) { swap(std::move(rr)); return *this; }
>
> I'm really surprised to see rvalue references in this code. Aren't
> you talking about move _emulation_ here? If you have rvalue
> references, why not just use real move semantics?

That's probably a good question. On the one hand, I intended (i.e. I tried) to use real move semantics for simple_class. On the other hand, the reason I presented simple_class to the list at all really is closely related to "not" using real move semantics.

Let's assume I would have written the assignment operator as

self_t& operator=(self_t s) { swap(*this,s); return *this; }

and renounced on implementing an explicit move assignment operator (for example because the compiler complained about operator= being ambiguous). Then writing

(i)
simple_class_instance = boost::move(simple_class());

would be less efficient than

(ii)
simple_class_instance = simple_class();

because for (i) both the default constructor and the move constructor of simple_class will be called, but for (ii) only the default constructor of simple_class gets called. This is the point where I wished to have an explicit move assignment operator in simple_class. In addition, I realized that I could even improve the efficiency of my copy assignment operator by using the move assignment operator instead of swap:

self_t& operator=(self_t s) { return operator=(std::move(s)); }

OK, this doesn't compile (operator= is ambiguous), but

self_t& operator=(const self_t& cr) { return operator=(self_t(s)); }

compiles and has exactly the same effect, as explained in my previous mail. The important consequences for move _emulation_ are that there is a reasonable simple way to write the assignment operator such that "simple_class_instance = boost::move(simple_class());" won't be less efficient than "simple_class_instance = simple_class();". This statement remains true even for more efficient implementations of "self_t& operator=(const self_t& cr)". So breaking automatic move assignment from temporaries is really a sensible option.

Now for using Boost.Move without rvalue references, things are a bit different, because

self_t& operator=(self_t s) { return operator=(boost::move(s)); }
self_t& operator=(BOOST_RV_REF<self_t> rr) { ...; return *this; }

compiles without error and does exactly what it should, while

self_t& operator=(const self_t& cr) { return operator=(self_t(s)); }
self_t& operator=(BOOST_RV_REF<self_t> rr) { ...; return *this; }

will probably compile (the compiler might issue a warning about a non-terminating recursion), but is badly broken.

The consequence for Boost.Move is that 3 possible move emulation modes are available, each with its own drawbacks when used without true rvalue references:

a) A mode that allows reusing existing resources in the copy assignment operator and automatically move assigns from temporaries. However, it modifies the signature of automatically generated copy assignment operators of classes containing such a class in an unwanted way.
b) A mode that allows reusing existing resources in the copy assignment operator, but doesn't automatically move assigns from temporaries.
c) A mode that doesn't allow reusing existing resources in the copy assignment operator, but automatically move assigns from temporaries.

When used with true rvalue references, the limitations of modes a and b go away.

Regards,
Thomas


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