Boost logo

Boost :

From: Vaclav Vesely (vaclav.vesely_at_[hidden])
Date: 2006-03-16 16:29:55


Howard Hinnant napsal(a):
>> Given a type T, a non-initialized x, and an initialized y, here is
>> the sequence of statements you're optimizing.
>>
>> ::new(&x) T(move(y));
>> y.~T();
>
> The better name finally occurred to me this morning. If the above
> were to be proposed for the C++ language, here is the syntax I would
> expect:
>
> y.~T(&x);
>
> I.e. this is a "move destructor". The compiler could even safely
> auto-generate it using the move-construct-destruct sequence. Anyway,
> therefore the name I recommend is:
>
> has_trivial_move_destructor.

Howard, thank you very match for your suggestions. I didn't know about
move semantics proposal so far.

There are three related operation to optimize:

        template<typename Type>
        void move(Type& dst, Type& src)

Default implementation for move is copy assignment. But for each type
should be safe alternative implementation by swap. Maybe the swap could
be good default implementation for types without trivial copy
constructor. I can't imagine an example of a type with non-trivial
constructor, where copy implementation would be more effective.

        template<typename Type>
        void move_construct(Type& dst, Type& src)

Default implementation is copy construct. For classes with default
constructor can be move effective to default construct and than swap.
For specialize this function for 3rd party types (STL for example) it
would be handy to be able to declare, that we prefer
default-construct-swap alternative but to leave at compiler to decide if
there is an default constructor and so if it's possible. Unfortunately I
  don't know, how to write has_default_constructor trait. I tried to use
concept_check but it seems to be a wrong way. Maybe it's not possible
without help from compiler. Any idea?

        template<typename Type>
        void move_destruct(Type& dst, Type& src)

It simply calls move_construct and than destructor the src.

In the attached test there is a trait "use_swap_for_move" and
"use_swap_for_move_construct". The first is false by default, the second
  takes over the value of the first. Thus it's possible to change
implementation of all three functions by specializing only the
"use_swap_for_move" trait (typical usage). But for
non-default-constructible types it's possible to change only the
implementation of the move function.

I eagerly await more advice and suggestions.

Regards,
Vaclav


#define BOOST_TEST_MAIN
#include "boost/test/unit_test.hpp"
#include <iostream>
#include <string>
#include "boost/type_traits.hpp"
#include "boost/utility/enable_if.hpp"

//-----------------------------------------------------------------------------
namespace boost {

template<typename Type>
struct use_swap_for_move
    : public false_type
{
};

template<typename Type>
struct use_swap_for_move_construct
    : public use_swap_for_move<typename Type>
{
};

namespace detail
{
    // Move by copy
    template<typename Type, typename Enable = void>
    struct move_impl
    {
        static void impl(Type& dst, Type& src)
        {
            dst = src;
        }
    };

    // Move by swap
    template<typename Type>
    struct move_impl<Type,
        typename enable_if_c<use_swap_for_move<Type>::value>::type>
    {
        static void impl(Type& dst, Type& src)
        {
            swap(dst, src);
        }
    };

    // Move construct by copy construct
    template<typename Type, typename Enable = void>
    struct move_construct_impl
    {
        static void impl(Type& dst, Type& src)
        {
            new (&dst) Type(src);
        }
    };

    // Move construct by default construct and move
    template<typename Type>
    struct move_construct_impl<Type,
        typename enable_if_c<use_swap_for_move_construct<Type>::value>::type>
    {
        static void impl(Type& dst, Type& src)
        {
            new (&dst) Type();
            move(dst, src);
        }
    };
}

template<typename Type>
void move(Type& dst, Type& src)
{
    detail::move_impl<Type>::impl(dst, src);
}

template<typename Type>
void move_construct(Type& dst, Type& src)
{
    detail::move_construct_impl<Type>::impl(dst, src);
}

template<typename Type>
void move_destruct(Type& dst, Type& src)
{
    move_construct(dst, src);
    src.~Type();
}

} // namespace boost

//-----------------------------------------------------------------------------
// Spy classes - can by empty or filled with imaginary data
// Counts all "allocated" data in static memeber for testing purposes

// basic spy - moves by copy
struct spy
{
    spy(bool data = false)
        : m_data(data)
    {
        std::cout << "\tnew(data = " << m_data << ")" << std::endl;
        if(m_data) data_count++;
    }

    spy(const spy& other)
        : m_data(other.m_data)
    {
        std::cout << "\tnew(data = " << m_data << ") copied" << std::endl;
        if(m_data) data_count++;
    }

    ~spy()
    {
        std::cout << "\tdestroy(data = " << m_data << ")" << std::endl;
    }

    spy& operator=(const spy& other)
    {
        m_data = other.m_data;
        std::cout << "\tassign(data = " << m_data << ")" << std::endl;
        if(m_data) data_count++;
        return *this;
    }

    void swap(spy& other)
    {
        std::cout << "\tswap(data = " << m_data << ", " << other.m_data << ")"
            << std::endl;
        std::swap(m_data, other.m_data);
    }

    bool m_data;

    static int data_count;

};

int spy::data_count = 0;

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

//-----------------------------------------------------------------------------
// swap spy - moves and move construct by swap

struct swap_spy:
    public spy
{
    swap_spy(bool data = false)
        : spy(data)
    {
    }

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

template<>
struct boost::use_swap_for_move<swap_spy>:
    public true_type
{
};

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

struct nondfltconstr_spy:
    public spy
{
    nondfltconstr_spy(bool data)
        : spy(data)
    {
    }

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

template<>
struct boost::use_swap_for_move<nondfltconstr_spy>:
    public true_type
{
};

template<>
struct boost::use_swap_for_move_construct<nondfltconstr_spy>:
    public false_type
{
};

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

BOOST_AUTO_TEST_CASE(move_by_copy)
{
    std::cout << "--------------------------------" << std::endl;
    std::cout << "move_by_copy" << std::endl;
    spy::data_count = 0;
    {
        spy src(true);
        spy dst;
        boost::move(dst, src);
    }
    std::cout << "data_count = " << spy::data_count << std::endl;
    BOOST_CHECK_EQUAL(spy::data_count, 2);
}

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

BOOST_AUTO_TEST_CASE(move_by_swap)
{
    std::cout << "--------------------------------" << std::endl;
    std::cout << "move_by_swap" << std::endl;
    spy::data_count = 0;
    {
        swap_spy src(true);
        swap_spy dst;
        boost::move(dst, src);
    }
    std::cout << "data_count = " << spy::data_count << std::endl;
    BOOST_CHECK_EQUAL(spy::data_count, 1);
}

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

BOOST_AUTO_TEST_CASE(move_destruct)
{
    std::cout << "--------------------------------" << std::endl;
    std::cout << "move_destruct" << std::endl;
    spy::data_count = 0;
    {
        char src[sizeof(swap_spy)];
        char dst[sizeof(swap_spy)];
        new (&src) swap_spy(true);
        boost::move_destruct(reinterpret_cast<swap_spy&>(dst),
            reinterpret_cast<swap_spy&>(src));
        reinterpret_cast<swap_spy&>(dst).~swap_spy();
    }
    std::cout << "data_count = " << spy::data_count << std::endl;
    BOOST_CHECK_EQUAL(spy::data_count, 1);
}

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

BOOST_AUTO_TEST_CASE(noncopyable_move)
{
    std::cout << "--------------------------------" << std::endl;
    std::cout << "noncopyable_move" << std::endl;
    spy::data_count = 0;
    {
        nondfltconstr_spy src(true);
        nondfltconstr_spy dst(true);
        boost::move(dst, src);
    }
    std::cout << "data_count = " << spy::data_count << std::endl;
    BOOST_CHECK_EQUAL(spy::data_count, 2);
}

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

BOOST_AUTO_TEST_CASE(noncopyable_move_construct)
{
    std::cout << "--------------------------------" << std::endl;
    std::cout << "nondfltconstr_move_construct" << std::endl;
    spy::data_count = 0;
    {
        char src[sizeof(nondfltconstr_spy)];
        char dst[sizeof(nondfltconstr_spy)];
        new (&src) nondfltconstr_spy(true);
        boost::move_construct(reinterpret_cast<nondfltconstr_spy&>(dst),
            reinterpret_cast<nondfltconstr_spy&>(src));
        reinterpret_cast<nondfltconstr_spy&>(dst).~nondfltconstr_spy();
        reinterpret_cast<nondfltconstr_spy&>(dst).~nondfltconstr_spy();
    }
    std::cout << "data_count = " << spy::data_count << std::endl;
    BOOST_CHECK_EQUAL(spy::data_count, 2);
}

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


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