Re: [boost] [optional] swapping initialized with uninitialized

Ulrich Eckhardt wrote:
On Friday 10 March 2006 17:14, Vaclav Vesely wrote:
When two optionals (one initialized and other not) are swaped, then the one value in uninitialized optional is constructed and the other one in initialized optional is destructed.
IMHO it's not necessary. It should be more effective to swap optional's m_storages without any values constructiona and destruction.
It could only be done if the object was moveable, i.e. depending on a type_trait. Looking at the Boost type_traits library, I wonder why this
doesn't exist - many things can be sped up using memcpy when is_pod is
... trait true,
but even things like vector/string/deque (list/map/set depending on whether their root node is stored by pointer) can be moved in memory without negative effects (yes, talking about typical implementations here, not guarantees).
I see now. But IMHO is_pod is not satisfactory condition for is_movable. For example the class struct X { X* me; // Can points to the instance }; seems to be POD, but it's not movable. The best implicit implamentation is probably the has_trivial_copy trait. For each other moveble type must be is_movable explicitly specialized. (BTW for compiler writers: compiler support for test a trait on all class members would be very useful for many such traits. And existing compilers with support of has_trivial_copy and similar traits must use this technique internally.) I think it worth it. For example I use optional<string> frequently. If you think that the is_movable trait is good enogh for Boost I would like write it including test and documentation. Regards, Vaclav

"Václav Veselý" wrote:
struct X { X* me; // Can points to the instance };
seems to be POD, but it's not movable. The best implicit implamentation is probably the has_trivial_copy trait. For each other moveble type must be is_movable explicitly specialized. (BTW for compiler writers: compiler support for test a trait on all class members would be very useful for many such traits. And existing compilers with support of has_trivial_copy and similar traits must use this technique internally.)
I think it worth it. For example I use optional<string> frequently. If you think that the is_movable trait is good enogh for Boost I would like write it including test and documentation.
Nonstandard Template Library (http://www.ntllib.org/) uses extensively this feature (http://www.ntllib.org/moveable.html). One of authors, Tomas Fidler, said that moveable has noticeable impact on performance and that absolute majority of classes he wrote are moveable, so he uses it as default option. /Pavel _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Saturday 11 March 2006 20:19, Václav Veselý wrote:
Ulrich Eckhardt wrote:
On Friday 10 March 2006 17:14, Vaclav Vesely wrote:
When two optionals (one initialized and other not) are swaped, then the one value in uninitialized optional is constructed and the other one in initialized optional is destructed.
IMHO it's not necessary. It should be more effective to swap optional's m_storages without any values constructiona and destruction.
...
It could only be done if the object was moveable, i.e. depending on a type_trait. Looking at the Boost type_traits library, I wonder why this trait doesn't exist - many things can be sped up using memcpy when is_pod is true, but even things like vector/string/deque (list/map/set depending on whether their root node is stored by pointer) can be moved in memory without negative effects (yes, talking about typical implementations here, not guarantees).
I see now. But IMHO is_pod is not satisfactory condition for is_movable. For example the class
struct X { X* me; // Can points to the instance };
seems to be POD, but it's not movable.
Well, in the case that 'me' must always be equal to 'this', then this thing is not even copyable. Otherwise, if it had a ctor/cctor, it wouldn't be a POD anymore. PODs can be copied using memcpy(), so, as my arguing goes, they can also be moved that way.
The best implicit implamentation is probably the has_trivial_copy trait.
I agree, has_trivial_copy catches all PODs but also a few more that are not PODs e.g. because they have a nontrivial ctor or dtor.
I think it worth it. For example I use optional<string> frequently. If you think that the is_movable trait is good enogh for Boost I would like write it including test and documentation.
I also do think it is worth it (note to self: see if STLport has it already, think about std::vector), what do others think? Uli

"Ulrich Eckhardt" wrote:
The best implicit implamentation is probably the has_trivial_copy trait.
I think it worth it. For example I use optional<string> frequently. If you think that the is_movable trait is good enogh for Boost I would like write it including test and documentation.
I also do think it is worth it (note to self: see if STLport has it already, think about std::vector), what do others think?
If you implement it in STLport, would you be able to get some performance data from tests? The NTL claims large (for what I remember) performance gains. /Pavel PS: is it possible to downloas somehow STLport 5.0x branch for BCB? I was not able to do it no matter what.

Hi, draft of the is_movable trait is attached. Notes: - Implicit implementation is based on the has_trivial_copy trait. - For STL configurations, which defines BOOST_HAS_MOVABLE_STL, is_movable.hpp declares STL containers (and their const versions) as movable. Partial template specialization is required (!defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)). Open issues: - I'm not sure with volatiles. has_trivial_copy declares volatile types explicitly as not has_trivial_copy. I don't know why but is_movable should probably follow this. - I have tested MSVC (6, 7.1 and 8) and GCC (3.2.3 - MinGW) compilers. MSVC is fine but GCC complains with specializations for STL types. It seems to be related to a different macro expansion strategy of preprocessor. I'm not able to use BOOST_PP_COMMA macro correctly. I will appreciate any help. Regards, Vaclav

On Mar 13, 2006, at 8:42 AM, Vaclav Vesely wrote:
Hi,
draft of the is_movable trait is attached.
Notes:
- Implicit implementation is based on the has_trivial_copy trait.
- For STL configurations, which defines BOOST_HAS_MOVABLE_STL, is_movable.hpp declares STL containers (and their const versions) as movable. Partial template specialization is required (!defined (BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)).
Open issues:
- I'm not sure with volatiles. has_trivial_copy declares volatile types explicitly as not has_trivial_copy. I don't know why but is_movable should probably follow this.
- I have tested MSVC (6, 7.1 and 8) and GCC (3.2.3 - MinGW) compilers. MSVC is fine but GCC complains with specializations for STL types. It seems to be related to a different macro expansion strategy of preprocessor. I'm not able to use BOOST_PP_COMMA macro correctly. I will appreciate any help.
"is_movable" is an overly vague name for what you're attempting to detect. 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(); In the current nomenclature (the move proposal before the committee), there are terms such as "move constructor" and "move assignment". In both of these cases the source is left in a constructed state. It would be very easy to confuse the meaning of "is_movable" with these existing definitions. I don't know what a good name would be, but I tend to think that the following would be getting closer: has_trivial_move_constructor_destructor or: has_trivial_destructive_move_constructor BOOST_HAS_MOVABLE_STL seems overly simplistic, and perhaps even dangerous. Overly simplistic in that for some implementations (e.g. gcc 4.x) some of the containers will have a trivial destructive move constructor and some containers won't. Additionally, for containers that store an Allocator, Comparator, or other policy class, their eligibility (if potentially eligible in the first place), should depend upon the eligibility of the Allocator and/or Comparator (and because of this std::comparators need to be treated - std::less, etc.). And in at least one implementation I'm aware of, eligibility for even something as simple as vector<char> depends on whether you're using the debugging version of the STL. In the debugging version the vector holds a list of outstanding iterators which in turn point back to the vector, making it ineligible. The last point highlights the danger aspect. As currently set up, you can silently get a false positive for eligibility just by switching into debug mode (on at least one implementation). The other danger is that vendors have been known to change the eligibility of some containers from release to release (examples include gcc node-based containers). Since this trait can't be auto- detected for things as complicated as containers, I recommend the trait only be turned on for specific version numbers of a std::lib, and that a true response not be forward compatible (later versions need to be specifically set to true). If this trait is to exist, you can't go wrong with including pair, and perhaps even tuple. At the very least their implementation is entertaining (at least tuple's). :-) -Howard

On Mar 13, 2006, at 10:40 AM, Howard Hinnant wrote:
"is_movable" is an overly vague name for what you're attempting to detect.
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

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

On Mar 16, 2006, at 4:29 PM, Vaclav Vesely wrote:
Howard, thank you very match for your suggestions. I didn't know about move semantics proposal so far.
My pleasure Vaclav. I think your code is close to optimum for what can be achieved today, and indeed looks quite similar to what I put into the CodeWarrior STL to simulate move semantics (before I had language support to do it right). I well understand your desire to see these kinds of optimizations today, as opposed to years from now.
There are three related operation to optimize:
template<typename Type> void move(Type& dst, Type& src)
You might consider calling this one move_assign. That gives you a nice symmetry to your naming scheme.
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.
One example is pair<string, int>. :-( Admittedly this is because pair doesn't have a swap and should (and I will do my best to see to it that future std::pair does have swap - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/ n1856.html#20.2.3%20-%20Pairs ). But the example stands: Any class that holds a heavy type where the designer neglected to give the class a swap will be pessimized by a move_assign that swaps instead of copies. I've also seen rather simple classes that are given copy constructors when the compiler-generated copy constructor would have been both trivial and correct. Admittedly that's a (performance) bug, but I've seen it happen often enough. I think I'd stick with a default implementation of copy assignment.
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?
Sorry, I'm afraid I don't have any ideas short of compiler help. Other reasonable optimizations (for some classes) are a memcpy followed by memset the source to 0 (or equivalent). This is just a documentation suggestion for you, not a recommendation for you to make that the default. Some classes are specifically designed to default construct (or otherwise have a valid state) with all bits 0 (one of my favorite std::string implementations was designed with this in mind. ;-) Also for empty allocators, probably every std::vector could use that technique).
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.
You've got a convenience - performance tradeoff going here. What you have seems most convenient. However the memcpy/memset algorithm, especially if it doesn't actually call memcpy and memset, is going to be faster than default construct then swap for some types. Perhaps that just adds up to a documentation issue? As an example: // assume vector is a friend, empty allocator assumed template <class T> inline void move_construct(std::vector<T>& dst, std::vector<T>& src) { dst.begin_ = src.begin_; dst.end_ = src. end_; dst.last_ = src. last_; src.begin_ = 0; src.end_ = 0; src.last_ = 0; } 3 reads, 6 writes. Versus: template <class T> inline void move_construct(std::vector<T>& dst, std::vector<T>& src) { // default construct dst dst.begin_ = 0; dst.end_ = 0; dst.last_ = 0; // swap src, dst tmp.begin_ = src.begin_; tmp.end_ = src. end_; tmp.last_ = src. last_; src.begin_ = dst.begin_; src.end_ = dst.end_; src.last_ = dst.last_; dst.begin_ = tmp.begin_; dst.end_ = tmp.end_; dst.last_ = tmp.last_; } 9 reads, 12 writes, which looks over 2x slower. With luck your optimizer might store all three words of tmp in registers (on a sufficiently register-rich platform) and that would bring your cost down to: 6 reads, 9 writes, which is still at least 50% slower than the memcpy/ memset algorithm. Good luck with your library. I hope to make it obsolete in C++0X! :-) -Howard

Howard Hinnant wrote:
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.
One example is pair<string, int>. :-( Admittedly this is because pair doesn't have a swap ...
My current move implementation for pairs uses explicit move functions for pairs. But wouldn't be better to specialize std::swap for pairs in Boost.Utility until the new STL would contain it? The same for tuples. I can write special move functions for them. But adding swap for tuples and then reuse it for moving seems to be more useful.
Default implementation is copy construct. For classes with default constructor can be move effective to default construct and than swap. ... You've got a convenience - performance tradeoff going here. What you have seems most convenient. However the memcpy/memset algorithm, especially if it doesn't actually call memcpy and memset, ...
How can I use memecpy/memset algorithm without call them?
... is going to be faster than default construct then swap for some types. Perhaps that just adds up to a documentation issue? ... [an example with vector] 3 reads, 6 writes. Versus: 9 reads, 12 writes, which looks over 2x slower.
For me this is not crucial difference. What I'm trying to optimize is copying of the contents of containers which can be arbitrary large. However adding support for memcpy is easy so it can be there ready for use. But I'm not sure with usage. When I have a class which can be moved by memcpy, is it correct to set has_trivial_move_assign/constructor? Or has_trivial traits should mean, that the corresponding member functions are automatically generated by compiler? Regards, Vaclav

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. I think the files in the yahoo group are as far as I got... http://groups.yahoo.com/group/boost/files/move.tar.gz Hamish

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>()); } } //-----------------------------------------------------------------------------
participants (6)
-
Hamish Mackenzie
-
Howard Hinnant
-
Pavel Vozenilek
-
Ulrich Eckhardt
-
Vaclav Vesely
-
Václav Veselý