Boost logo

Boost :

From: brangdon_at_[hidden]
Date: 2002-02-06 18:44:16


In-Reply-To: <69857FD5-1A59-11D6-995A-003065D18932_at_[hidden]>
On Tue, 5 Feb 2002 11:57:17 -0500 Howard Hinnant (hinnant_at_[hidden])
wrote:
> Yup! A minimum of 2 types of move are absolutely necessary:
>
> 1. Move construct.
> 2. Move assign.
>
> One could argue that 2 more moves are useful:
>
> 3. Move construct, destructing the source.
> 4. Move assign, destructing the source.

Can you explain the semantics of these more explicitly? I don't understand
why we need more than (3). (2) seems to be very similar to either swap()
or plain operator=(). (1) seems to need to allocate and copy resources, so
cannot be exception safe or especially efficient. (3) doesn't seem to
offer much over swap() and destruct.

To me (3) is only absolutely necessary one. What am I missing?

I would suggest adding a block move which encapsulates the exception
handling. The default can be like:

   template <typename T>
   void block_move( T *pDest, T *pSrc, int count ) {
       int moved;
       try {
           for (moved = 0; moved != count; ++moved)
               Construct( pDest+moved, pSrc[moved] );
       }
       catch (...) {
           while (--moved >= 0)
               Destruct( pDest + moved );
           throw;
       }
       for (int i = 0; i != count; ++i)
           Destruct( pSrc + i );
   }

On entry, pDest points to raw bytes and pSrc to initialised objects. On
exit, it is the other way about. All of the objects are copied before any
of them are destroyed. This offers the strong guarantee, provided T's
destructor does not throw (and the memory blocks don't overlap).

The specialisation for PODs can use memmove(). The specialisation for
user-defined classes can use a variety of techniques, as being discussed
elsewhere. If these cases use no-throw operations, no exception handling
is required. Block_move() can encapsulate the knowledge of whether
exception handling is needed as well as the logic itself.

I think block_move() is in some ways a better basic utility than move().
We can move a single object by passing block_move() a count of 1, and a
reasonably good compiler should optimise away the loops. When we do have
to move many items, we get better efficiency by working at this level of
granularity. The main advantage is that this is the most natural level to
deal with exception safety.

Much of this stuff was discussed in comp.lang.c++.moderated, in the first
two months of 2001. See for example the thread entitled "Takeover
constructors" on 21 Jan 2001.

-- Dave Harris


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