
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); } //-----------------------------------------------------------------------------