Boost logo

Boost :

From: Vaclav Vesely (vaclav.vesely_at_[hidden])
Date: 2006-03-28 15:58:21


Hamish Mackenzie wrote:
> I had a go at something similar back in 2002, but I had no compelling
> need for it (and it seemed like most people were happy to wait for move
> constructors) so I left it.

Thank you, it inspired me and I adopt some of your ideas, especially
concerning move traits.

I have improved moving stuff. No it has following features:

- three predefined implementations for move (by copy (default), by swap
and by memcpy)

- STL types use move by swap

- std::pair and boost::tuple use move by items

Preliminary documentation is in move.txt. The code is tested only on
MSVC 7.1 and 8.0. MinGW seems to work except of tuples.

My motivation is using move for swapping optionals. But I've found some
move code in variant library too (but I didn't inspect).

Are authors of that libraries willing to use this unified move
algorithms? Is the current design satisfactory for that purpose?

Should be move_traits types be in the type_traits or in the utility/move?

Regars,
Vaclav

Using move operations:

- move-assign

    string src("abc");
    string dst;
    move_assign(dst, src);

- move-construct

    string src("abc");
    char dst[sizeof(string)];
    move_construct(reinterpret_cast<string&>(dst), src);

- move-destruct

    char src[sizeof(string)];
    char dst[sizeof(string)];
    new (&src) string("abc");
    move_destruct(reinterpret_cast<string&>(dst),
        reinterpret_cast<string&>(src));

There are following predefined implementations of three move operations:

- move-assign:
    - by copy-assign (default)
    - by swap
    - by memcpy

- move-construct:
    - by copy-construct (default)
    - by default-construct and move-asssign
    - by memcpy

- move-destruct:
    - by move-construct and destruct (default)
    - by memcpy

You have several options, how to define move_traits for your type:

1. Using one of predefined set of move traits:

    // my_type has swap and default constructor
    template<>
    struct move_traits<my_type>:
        public move_traits_by_swap<my_type>
    {
    };

2. Combine various implementations for individual operations:

    // my_type has swap but no default constructor
    template<>
    struct move_traits<my_type>:
        public move_assign_traits_by_swap<my_type>, // assign by swap
        public move_construct_traits_by_copy<my_type>, // construct by copy
        public move_destruct_traits<my_type> // use default destruct
    {
    };
  
3. Using one of predefined set of move traits and override some of them:
 
    // std::pair uses default swap
    
    template<typename First, typename Second>
    struct move_traits<std::pair<First, Second> >:
        public move_traits_by_swap<std::pair<First, Second> >
    {
        static void do_move_assign(
            std::pair<First, Second>& dst,
            std::pair<First, Second>& src)
        {
            move_assign(dst.first, src.first);
            move_assign(dst.second, src.second);
        }
    };

4. Write move traits from scratch

    template<>
    struct move_traits<my_type>
    {
        static void do_move_assign(...) ...
        static void do_move_construct(...) ...
        static void do_move_destruct(...) ...
    }


// (C) Copyright Vaclav Vesely 2006.
// Use, modification and distribution are subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt).
//
// See http://www.boost.org/utility for most recent version including
// documentation.

#ifndef BOOST_UTILITY_MOVE_HPP_INCLUDED
#define BOOST_UTILITY_MOVE_HPP_INCLUDED

//-----------------------------------------------------------------------------

namespace boost {

    //-------------------------------------------------------------------------
    // move_assign_traits

    // move assign by copy assign
    template<typename Type>
    struct move_assign_traits_by_copy
    {
        static void do_move_assign(Type& dst, Type& src)
        {
            dst = src;
        }
    };

    // move assign by swap
    template<typename Type>
    struct move_assign_traits_by_swap
    {
        static void do_move_assign(Type& dst, Type& src)
        {
            swap(dst, src);
        }
    };

    // move assign by memcpy
    template<typename Type>
    struct move_assign_traits_by_memcpy
    {
        static void do_move_assign(Type& dst, Type& src)
        {
            memcpy(reinterpret_cast<void*>(&dst),
                reinterpret_cast<void*>(&src), sizeof(src));
        }
    };

    // move assign by copy is default
    template<typename Type>
    struct move_assign_traits:
        public move_assign_traits_by_copy<Type>
    {
    };

    //-------------------------------------------------------------------------
    // move_construct_traits

    // move construct by copy
    template<typename Type>
    struct move_construct_traits_by_copy
    {
        static void do_move_construct(Type& dst, Type& src)
        {
            new (&dst) Type(src);
        }
    };

    // move construct by default construct and move assign
    template<typename Type>
    struct move_construct_traits_by_move_assign
    {
        static void do_move_construct(Type& dst, Type& src)
        {
            new (&dst) Type();
            move_assign(dst, src);
        }
    };

    // move construct by memcpy
    template<typename Type>
    struct move_construct_traits_by_memcpy
    {
        static void do_move_construct(Type& dst, Type& src)
        {
            memcpy(reinterpret_cast<void*>(&dst),
                reinterpret_cast<void*>(&src), sizeof(src));
        }
    };

    // move construct by copy is default
    template<typename Type>
    struct move_construct_traits:
        public move_construct_traits_by_copy<Type>
    {
    };

    //-------------------------------------------------------------------------
    // move_destruct_traits

    // move destruct by move construct and destruct
    template<typename Type>
    struct move_destruct_traits_by_move_construct
    {
        static void do_move_destruct(Type& dst, Type& src)
        {
            move_construct(dst, src);
            src.~Type();
        }
    };

    // move destruct by memcpy
    template<typename Type>
    struct move_destruct_traits_by_memcpy
    {
        static void do_move_destruct(Type& dst, Type& src)
        {
            memcpy(reinterpret_cast<void*>(&dst),
                reinterpret_cast<void*>(&src), sizeof(src));
        }
    };

    // move construct by move construct and destruct is default
    template<typename Type>
    struct move_destruct_traits:
        public move_destruct_traits_by_move_construct<Type>
    {
    };

    //-------------------------------------------------------------------------
    // move_traits

    template<typename Type>
    struct move_traits_by_copy:
        public move_assign_traits_by_copy<Type>,
        public move_construct_traits_by_copy<Type>,
        public move_destruct_traits_by_move_construct<Type>
    {
    };

    template<typename Type>
    struct move_traits_by_swap:
        public move_assign_traits_by_swap<Type>,
        public move_construct_traits_by_move_assign<Type>,
        public move_destruct_traits_by_move_construct<Type>
    {
    };

    template<typename Type>
    struct move_traits_by_memcpy:
        public move_assign_traits_by_memcpy<Type>,
        public move_construct_traits_by_memcpy<Type>,
        public move_destruct_traits_by_memcpy<Type>
    {
    };

    template<typename Type>
    struct move_traits:
        public move_assign_traits<Type>,
        public move_construct_traits<Type>,
        public move_destruct_traits<Type>
    {
    };

    //-------------------------------------------------------------------------

    template<typename Type>
    void move_assign(Type& dst, Type& src)
    {
        move_traits<Type>::do_move_assign(dst, src);
    }

    template<typename Type>
    void move_construct(Type& dst, Type& src)
    {
        move_traits<Type>::do_move_construct(dst, src);
    }

    template<typename Type>
    void move_destruct(Type& dst, Type& src)
    {
        move_traits<Type>::do_move_destruct(dst, src);
    }

} // namespace boost

//-----------------------------------------------------------------------------
// STL types

namespace std {

    template<typename CharType, typename Traits, typename Allocator>
    class basic_string;

    template<typename Type>
    class complex;

    template<typename Type, typename Allocator>
    class deque;

    template<typename Type, typename Allocator>
    class list;

    template<typename Key, typename Type, typename Allocator, typename Compare>
    class map;

    template<typename Key, typename Type, typename Allocator, typename Compare>
    class multimap;

    template<typename Key, typename Allocator, typename Compare>
    class multiset;

    template<typename First, typename Second>
    struct pair;

    template<typename Type, typename Container, typename Compare>
    class priority_queue;

    template<typename Key, typename Allocator, typename Compare>
    class set;

    template<typename Type, typename Container>
    class stack;

    template<typename Type, typename Allocator>
    class vector;

    template<typename Type, typename Container>
    class queue;

} // namespace std

namespace boost {
    
    template<typename Type>
    struct move_traits<std::complex<Type> >:
        public move_traits_by_swap<std::complex<Type> >
    {
    };

    template<typename CharType, typename Traits, typename Allocator>
    struct move_traits<
            std::basic_string<CharType, Traits, Allocator> >:
        public move_traits_by_swap<
            std::basic_string<CharType, Traits, Allocator> >
    {
    };

    template<typename Type, typename Allocator>
    struct move_traits<std::deque<Type, Allocator> >:
        public move_traits_by_swap<std::deque<Type, Allocator> >
    {
    };

    template<typename Type, typename Allocator>
    struct move_traits<std::list<Type, Allocator> >:
        public move_traits_by_swap<std::list<Type, Allocator> >
    {
    };

    template<typename Key, typename Type, typename Allocator, typename Compare>
    struct move_traits<std::map<Key, Type, Allocator, Compare> >:
        public move_traits_by_swap<std::map<Key, Type, Allocator, Compare> >
    {
    };

    template<typename Key, typename Type, typename Allocator, typename Compare>
    struct move_traits<std::multimap<Key, Type, Allocator, Compare> >:
        public move_traits_by_swap<
            std::multimap<Key, Type, Allocator, Compare> >
    {
    };

    template<typename Key, typename Allocator, typename Compare>
    struct move_traits<std::multiset<Key, Allocator, Compare> >:
        public move_traits_by_swap<std::multiset<Key, Allocator, Compare> >
    {
    };

    template<typename First, typename Second>
    struct move_traits<std::pair<First, Second> >:
        public move_traits_by_swap<std::pair<First, Second> >
    {
        static void do_move_assign(
            std::pair<First, Second>& dst,
            std::pair<First, Second>& src)
        {
            move_assign(dst.first, src.first);
            move_assign(dst.second, src.second);
        }
    };

    template<typename Key, typename Allocator, typename Compare>
    struct move_traits<std::set<Key, Allocator, Compare> >:
        public move_traits_by_swap<std::set<Key, Allocator, Compare> >
    {
    };

    template<typename Type, typename Container>
    struct move_traits<std::stack<Type, Container> >:
        public move_traits_by_swap<std::stack<Type, Container> >
    {
    };

    template<typename Type, typename Allocator>
    struct move_traits<std::vector<Type, Allocator> >:
        public move_traits_by_swap<std::vector<Type, Allocator> >
    {
    };

    template<typename Type, typename Container>
    struct move_traits<std::queue<Type, Container> >:
        public move_traits_by_swap<std::queue<Type, Container> >
    {
    };

} // namespace boost

//-----------------------------------------------------------------------------
// tuple

namespace boost {

    namespace detail {

        template<class Dst, class Src>
        void tuple_move_assign(Dst& dst, Src& src)
        {
            move_assign(dst.get_head(), src.get_head());
            tuple_move_assign(dst.get_tail(), src.get_tail());
        }

        template<>
        void tuple_move_assign<tuples::null_type, tuples::null_type>(
            tuples::null_type& dst, tuples::null_type& src)
        {
        }

    }

    template<typename T0, typename T1, typename T2, typename T3, typename T4,
        typename T5, typename T6, typename T7, typename T8, typename T9>
    class tuple;

    template<typename T0, typename T1, typename T2, typename T3, typename T4,
        typename T5, typename T6, typename T7, typename T8, typename T9>
    struct move_traits<
            tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >:
        public move_traits_by_swap<
            tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
    {
        static void do_move_assign(
            tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>& dst,
            tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>& src)
        {
            detail::tuple_move_assign(dst, src);
        }
    };

};

//-----------------------------------------------------------------------------

#endif // BOOST_UTILITY_MOVE_HPP_INCLUDED


#define BOOST_TEST_MAIN
#include <complex>
#include <deque>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <boost/assign/list_of.hpp>
#include <boost/range/functions.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/utility/move.hpp>

using namespace boost;
using namespace std;

//-----------------------------------------------------------------------------
// Spy classes - counts member function calls

// spy - moves by copy
struct spy
{

    spy()
        : m_data(0),
          m_not_copied_data(0)
    {
        default_construct_count++;
    }

    explicit spy(int data)
        : m_data(data),
          m_not_copied_data(data)
    {
        explicit_construct_count++;
    }

    spy(const spy& other)
        : m_data(other.m_data),
          m_not_copied_data(0)
    {
        copy_construct_count++;
    }

    ~spy()
    {
        destruct_count++;
    }

    spy& operator=(const spy& other)
    {
        m_data = other.m_data;
        copy_assign_construct_count++;
        return *this;
    }

    void swap(spy& other)
    {
        std::swap(m_data, other.m_data);
        swap_count++;
    }

    bool operator==(spy const& other) const
    {
        return m_data == other.m_data;
    }

    static void reset_counters()
    {
        default_construct_count = 0;
        explicit_construct_count = 0;
        copy_construct_count = 0;
        copy_assign_construct_count = 0;
        swap_count = 0;
        destruct_count = 0;
    }

    int m_data;
    int m_not_copied_data;

    static int default_construct_count;
    static int explicit_construct_count;
    static int copy_construct_count;
    static int copy_assign_construct_count;
    static int swap_count;
    static int destruct_count;

};

int spy::default_construct_count = 0;
int spy::explicit_construct_count = 0;
int spy::copy_construct_count = 0;
int spy::copy_assign_construct_count = 0;
int spy::swap_count = 0;
int spy::destruct_count = 0;

void swap(spy& left, spy& right)
{
    left.swap(right);
}

//-----------------------------------------------------------------------------
// swap spy - moves by swap

struct swap_spy:
    public spy
{
    swap_spy()
        : spy()
    {
    }

    explicit swap_spy(int data)
        : spy(data)
    {
    }

    swap_spy(const spy& other)
        : spy(other.m_data)
    {
    }
};

namespace boost {
    template<>
    struct move_traits<swap_spy>:
        public move_traits_by_swap<swap_spy>
    {
    };
}

//-----------------------------------------------------------------------------
// non default constructible spy - moves by swap, move construct by copy

struct nondfltconstr_spy:
    public spy
{
    explicit nondfltconstr_spy(int data)
        : spy(data)
    {
    }

    nondfltconstr_spy(const spy& other)
        : spy(other.m_data)
    {
    }
};

namespace boost {
    template<>
    struct move_traits<nondfltconstr_spy>:
        public move_assign_traits_by_swap<nondfltconstr_spy>,
        public move_construct_traits_by_copy<nondfltconstr_spy>,
        public move_destruct_traits<nondfltconstr_spy>
    {
    };
}

//-----------------------------------------------------------------------------
// memcpy_spy - moves by memcpy

struct memcpy_spy:
    public spy
{
    memcpy_spy()
        : spy()
    {
    }

    explicit memcpy_spy(int data)
        : spy(data)
    {
    }

    memcpy_spy(const spy& other)
        : spy(other.m_data)
    {
    }
};

namespace boost {
    template<>
    struct move_traits<memcpy_spy>:
        public move_traits_by_memcpy<memcpy_spy>
    {
    };
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(trivial_move_test)
{
    int src = 123;
    int dst = 0;

    boost::move_assign(dst, src);
    BOOST_CHECK_EQUAL(dst, 123);

    dst = 0;
    boost::move_construct(dst, src);
    BOOST_CHECK_EQUAL(dst, 123);

    dst = 0;
    boost::move_destruct(dst, src);
    BOOST_CHECK_EQUAL(dst, 123);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(move_assign_by_copy_test)
{
    spy::reset_counters();
    {
        spy src(123);
        spy dst;
        boost::move_assign(dst, src);
        BOOST_CHECK_EQUAL(dst.m_data, 123);
        BOOST_CHECK_EQUAL(dst.m_not_copied_data, 0);
    }
    BOOST_CHECK_EQUAL(spy::default_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::copy_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::copy_assign_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::swap_count, 0);
    BOOST_CHECK_EQUAL(spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(move_construct_by_copy_test)
{
    spy::reset_counters();
    {
        char src[sizeof(spy)];
        char dst[sizeof(spy)];
        new (&src) spy(123);
        boost::move_construct(reinterpret_cast<spy&>(dst),
            reinterpret_cast<spy&>(src));
        BOOST_CHECK_EQUAL(reinterpret_cast<spy&>(dst).m_data, 123);
        BOOST_CHECK_EQUAL(reinterpret_cast<spy&>(dst).m_not_copied_data, 0);
        reinterpret_cast<spy&>(src).~spy();
        reinterpret_cast<spy&>(dst).~spy();
    }
    BOOST_CHECK_EQUAL(spy::default_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::copy_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::swap_count, 0);
    BOOST_CHECK_EQUAL(spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(move_destruct_by_copy_test)
{
    spy::reset_counters();
    {
        char src[sizeof(spy)];
        char dst[sizeof(spy)];
        new (&src) spy(123);
        boost::move_destruct(reinterpret_cast<spy&>(dst),
            reinterpret_cast<spy&>(src));
        BOOST_CHECK_EQUAL(reinterpret_cast<spy&>(dst).m_data, 123);
        BOOST_CHECK_EQUAL(reinterpret_cast<spy&>(dst).m_not_copied_data, 0);
        reinterpret_cast<spy&>(dst).~spy();
    }
    BOOST_CHECK_EQUAL(spy::default_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::copy_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::swap_count, 0);
    BOOST_CHECK_EQUAL(spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(move_assign_by_swap_test)
{
    swap_spy::reset_counters();
    {
        swap_spy src(123);
        swap_spy dst;
        boost::move_assign(dst, src);
        BOOST_CHECK_EQUAL(dst.m_data, 123);
        BOOST_CHECK_EQUAL(dst.m_not_copied_data, 0);
    }
    BOOST_CHECK_EQUAL(swap_spy::default_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(swap_spy::copy_construct_count, 0);
    BOOST_CHECK_EQUAL(swap_spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(swap_spy::swap_count, 1);
    BOOST_CHECK_EQUAL(swap_spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(move_construct_by_swap_test)
{
    swap_spy::reset_counters();
    {
        char src[sizeof(swap_spy)];
        char dst[sizeof(swap_spy)];
        new (&src) swap_spy(123);
        boost::move_construct(reinterpret_cast<swap_spy&>(dst),
            reinterpret_cast<swap_spy&>(src));
        BOOST_CHECK_EQUAL(reinterpret_cast<swap_spy&>(dst).m_data, 123);
        BOOST_CHECK_EQUAL(reinterpret_cast<swap_spy&>(dst).m_not_copied_data,
            0);
        reinterpret_cast<swap_spy&>(src).~swap_spy();
        reinterpret_cast<swap_spy&>(dst).~swap_spy();
    }
    BOOST_CHECK_EQUAL(swap_spy::default_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(swap_spy::copy_construct_count, 0);
    BOOST_CHECK_EQUAL(swap_spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(swap_spy::swap_count, 1);
    BOOST_CHECK_EQUAL(swap_spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(move_destruct_by_swap_test)
{
    swap_spy::reset_counters();
    {
        char src[sizeof(swap_spy)];
        char dst[sizeof(swap_spy)];
        new (&src) swap_spy(123);
        boost::move_destruct(reinterpret_cast<swap_spy&>(dst),
            reinterpret_cast<swap_spy&>(src));
        BOOST_CHECK_EQUAL(reinterpret_cast<swap_spy&>(dst).m_data, 123);
        BOOST_CHECK_EQUAL(reinterpret_cast<swap_spy&>(dst).m_not_copied_data,
            0);
        reinterpret_cast<swap_spy&>(dst).~swap_spy();
    }
    BOOST_CHECK_EQUAL(swap_spy::default_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(swap_spy::copy_construct_count, 0);
    BOOST_CHECK_EQUAL(swap_spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(swap_spy::swap_count, 1);
    BOOST_CHECK_EQUAL(swap_spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(nondfltconstr_move_assign_test)
{
    nondfltconstr_spy::reset_counters();
    {
        nondfltconstr_spy src(123);
        nondfltconstr_spy dst(0);
        boost::move_assign(dst, src);
        BOOST_CHECK_EQUAL(dst.m_data, 123);
        BOOST_CHECK_EQUAL(dst.m_not_copied_data, 0);
    }
    BOOST_CHECK_EQUAL(nondfltconstr_spy::default_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 2);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::copy_construct_count, 0);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::swap_count, 1);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(nondfltconstr_move_construct_test)
{
    nondfltconstr_spy::reset_counters();
    {
        char src[sizeof(nondfltconstr_spy)];
        char dst[sizeof(nondfltconstr_spy)];
        new (&src) nondfltconstr_spy(123);
        boost::move_construct(reinterpret_cast<nondfltconstr_spy&>(dst),
            reinterpret_cast<nondfltconstr_spy&>(src));
        BOOST_CHECK_EQUAL(reinterpret_cast<nondfltconstr_spy&>(dst).m_data,
            123);
        BOOST_CHECK_EQUAL(reinterpret_cast<nondfltconstr_spy&>(dst)
            .m_not_copied_data, 0);
        reinterpret_cast<nondfltconstr_spy&>(src).~nondfltconstr_spy();
        reinterpret_cast<nondfltconstr_spy&>(dst).~nondfltconstr_spy();
    }
    BOOST_CHECK_EQUAL(nondfltconstr_spy::default_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::copy_construct_count, 1);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::swap_count, 0);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(nondfltconstr_move_destruct_test)
{
    nondfltconstr_spy::reset_counters();
    {
        char src[sizeof(nondfltconstr_spy)];
        char dst[sizeof(nondfltconstr_spy)];
        new (&src) nondfltconstr_spy(123);
        boost::move_destruct(reinterpret_cast<nondfltconstr_spy&>(dst),
            reinterpret_cast<nondfltconstr_spy&>(src));
        BOOST_CHECK_EQUAL(reinterpret_cast<nondfltconstr_spy&>(dst).m_data,
            123);
        BOOST_CHECK_EQUAL(reinterpret_cast<nondfltconstr_spy&>(dst)
            .m_not_copied_data, 0);
        reinterpret_cast<nondfltconstr_spy&>(dst).~nondfltconstr_spy();
    }
    BOOST_CHECK_EQUAL(nondfltconstr_spy::default_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::copy_construct_count, 1);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::swap_count, 0);
    BOOST_CHECK_EQUAL(nondfltconstr_spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(move_assign_by_memcpy_test)
{
    memcpy_spy::reset_counters();
    {
        memcpy_spy src(123);
        memcpy_spy dst;
        boost::move_assign(dst, src);
        BOOST_CHECK_EQUAL(dst.m_data, 123);
        BOOST_CHECK_EQUAL(dst.m_not_copied_data, 123);
    }
    BOOST_CHECK_EQUAL(memcpy_spy::default_construct_count, 1);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(memcpy_spy::copy_construct_count, 0);
    BOOST_CHECK_EQUAL(memcpy_spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(memcpy_spy::swap_count, 0);
    BOOST_CHECK_EQUAL(memcpy_spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(move_construct_by_memcpy_test)
{
    memcpy_spy::reset_counters();
    {
        char src[sizeof(memcpy_spy)];
        char dst[sizeof(memcpy_spy)];
        new (&src) memcpy_spy(123);
        boost::move_construct(reinterpret_cast<memcpy_spy&>(dst),
            reinterpret_cast<memcpy_spy&>(src));
        BOOST_CHECK_EQUAL(reinterpret_cast<memcpy_spy&>(dst).m_data, 123);
        BOOST_CHECK_EQUAL(reinterpret_cast<memcpy_spy&>(dst).m_not_copied_data,
            123);
        reinterpret_cast<memcpy_spy&>(src).~memcpy_spy();
        reinterpret_cast<memcpy_spy&>(dst).~memcpy_spy();
    }
    BOOST_CHECK_EQUAL(memcpy_spy::default_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(memcpy_spy::copy_construct_count, 0);
    BOOST_CHECK_EQUAL(memcpy_spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(memcpy_spy::swap_count, 0);
    BOOST_CHECK_EQUAL(memcpy_spy::destruct_count, 2);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(move_destruct_by_memcpy_test)
{
    memcpy_spy::reset_counters();
    {
        char src[sizeof(memcpy_spy)];
        char dst[sizeof(memcpy_spy)];
        new (&src) memcpy_spy(123);
        boost::move_destruct(reinterpret_cast<memcpy_spy&>(dst),
            reinterpret_cast<memcpy_spy&>(src));
        BOOST_CHECK_EQUAL(reinterpret_cast<memcpy_spy&>(dst).m_data, 123);
        BOOST_CHECK_EQUAL(reinterpret_cast<memcpy_spy&>(dst).m_not_copied_data,
            123);
        reinterpret_cast<memcpy_spy&>(dst).~memcpy_spy();
    }
    BOOST_CHECK_EQUAL(memcpy_spy::default_construct_count, 0);
    BOOST_CHECK_EQUAL(spy::explicit_construct_count, 1);
    BOOST_CHECK_EQUAL(memcpy_spy::copy_construct_count, 0);
    BOOST_CHECK_EQUAL(memcpy_spy::copy_assign_construct_count, 0);
    BOOST_CHECK_EQUAL(memcpy_spy::swap_count, 0);
    BOOST_CHECK_EQUAL(memcpy_spy::destruct_count, 1);
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(stl_test)
{
    // complex
    {
        complex<float> src(1, 2);
        complex<float> dst(4, 6);
        boost::move_assign(dst, src);
        BOOST_CHECK_EQUAL(dst, complex<float>(1, 2));
        BOOST_CHECK_EQUAL(src, complex<float>(4, 6));
    }

    // basic_string
    {
        string src("123");
        string dst("456");
        boost::move_assign(dst, src);
        BOOST_CHECK_EQUAL(dst, "123");
        BOOST_CHECK_EQUAL(src, "456");
    }

    // deque
    {
        deque<int> src_data = assign::list_of(1)(2)(3);
        deque<int> dst_data = assign::list_of(4)(5)(6);
        deque<int> src(src_data);
        deque<int> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src == dst_data);
    }

    // list
    {
        list<int> src_data = assign::list_of(1)(2)(3);
        list<int> dst_data = assign::list_of(4)(5)(6);
        list<int> src(src_data);
        list<int> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src == dst_data);
    }

    // map
    {
        map<int, string> src_data
            = assign::map_list_of(1, "a")(2, "b")(3, "d");
        map<int, string> dst_data
            = assign::map_list_of(4, "e")(5, "f")(6, "g");
        map<int, string> src(src_data);
        map<int, string> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src == dst_data);
    }

    // multimap
    {
        multimap<int, int> src_data = assign::map_list_of(1, 1)(2, 2)(3, 3);
        multimap<int, int> dst_data = assign::map_list_of(4, 4)(5, 5)(6, 6);
        multimap<int, int> src(src_data);
        multimap<int, int> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src == dst_data);
    }

    // multiset
    {
        multiset<int> src_data = assign::list_of(1)(2)(3);
        multiset<int> dst_data = assign::list_of(4)(5)(6);
        multiset<int> src(src_data);
        multiset<int> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src == dst_data);
    }

    // pair
    {
        pair<int, string> src_data(1, "abc");
        pair<int, string> dst_data(2, "efg");
        pair<int, string> src(src_data);
        pair<int, string> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src.first == src_data.first);
        BOOST_CHECK(src.second == dst_data.second);
    }
    
    // pair - move construct
    {
        typedef pair<int, string> pair_type;
        pair_type src_data(1, "abc");
        pair_type src(src_data);
        char dst[sizeof(pair_type)];
        boost::move_construct(reinterpret_cast<pair_type&>(dst), src);
        BOOST_CHECK(reinterpret_cast<pair_type&>(dst) == src_data);
        BOOST_CHECK(src.first == src_data.first);
        BOOST_CHECK(src.second == "");
        reinterpret_cast<pair_type&>(dst).~pair_type();
    }

    // set
    {
        set<int> src_data = assign::list_of(1)(2)(3);
        set<int> dst_data = assign::list_of(4)(5)(6);
        set<int> src(src_data);
        set<int> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src == dst_data);
    }

    // stack
    {
        stack<int> src_data = assign::list_of(1)(2)(3).to_adapter();
        stack<int> dst_data = assign::list_of(4)(5)(6).to_adapter();
        stack<int> src(src_data);
        stack<int> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src == dst_data);
    }

    // vector
    {
        vector<int> src_data = assign::list_of(1)(2)(3);
        vector<int> dst_data = assign::list_of(4)(5)(6);
        vector<int> src(src_data);
        vector<int> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src == dst_data);
    }

    // queue
    {
        queue<int> src_data = assign::list_of(1)(2)(3).to_adapter();
        queue<int> dst_data = assign::list_of(4)(5)(6).to_adapter();
        queue<int> src(src_data);
        queue<int> dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src == dst_data);
    }
}

//-----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(tuple_test)
{
    {
        typedef tuple<int, string, swap_spy> tuple_type;
        tuple_type src_data(1, "abc", swap_spy(1));
        tuple_type dst_data(2, "efg", swap_spy(2));
        tuple_type src(src_data);
        tuple_type dst(dst_data);
        boost::move_assign(dst, src);
        BOOST_CHECK(dst == src_data);
        BOOST_CHECK(src.get<0>() == src_data.get<0>());
        BOOST_CHECK(src.get<1>() == dst_data.get<1>());
        BOOST_CHECK(src.get<2>() == dst_data.get<2>());
    }
}

//-----------------------------------------------------------------------------


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