
On 11/01/2013 09:58, Thomas Jordan wrote:
Jeffrey Lee Hellrung, Jr. wrote
You didn't really expound on the implementation, but, if I had to guess, you'd also require your types to be default constructible, and it would perform suboptimally (worse than leaving the move call out) if the swap member function was equivalent to std::swap. I think this might preclude its use in generic contexts and limit its use to situations where you *know* the type is std::vector-like. Let me know if I'm presuming incorrectly :)
Yes, it certainly requires the types to be default constructible.
For a type with an expensive swap, performance would be suboptimal. The library checks at compile-time whether the element type of the boost array has a swap member, and calls array.swap if true, and copies otherwise (recursively if the element type is also a boost array). However, the user would need to write an overload for a type with an expensive member swap, which precludes the true generic context. So yes, it is more aimed at situations where you have pretty firm knowledge of the types you will be using. This would either require a really strong caveat emptor or may not be acceptable for release into the wild?
The alternative would be to have just the single, default, template move function:
//pseudo-code move(T& left, T& right)
plus a pair of template functions to move to and from temporaries respectively:
T move(T& x) void move(const T& from, T& to)
then have optimised move functions defined on a per-type basis, e.g.,
MyClass { void swap(MyClass& other){} friend void swap(MyClass& a, MyClass& b) { //efficient swap member } friend void move(MyClass& lhs, MyClass& rhs) { lhs.swap(rhs); } }
as well as overloads defined for any cheap-to-swap library types interested in:
void move(std::vector& lhs, std::vector& rhs){} void move(boost::function& lhs, boost::function& rhs){} etc.
Although this is safer in that it should ensure optimality - and is actually where I started - Here is the code for the safer version:
//SimpleMove.h #include <boost/type_traits/integral_constant.hpp> #include <boost/type_traits/is_scalar.hpp> #include <iterator> #include <algorithm> //namespace for overloads of move() function namespace Overloads { //Default - move as copy template <typename T> inline void move(T& from, T& to) { T tmp(from); from = to; to = tmp; } } //namespace for implementation functions namespace SimpleMove_Impl { template <typename T> inline void move_impl(T& from, T& to) { using ::Overloads::move; //bring this into scope move(from, to); } template<typename I, typename O> inline O move_impl(I f, I l, O result, ::boost::true_type) { return std::copy(f,l,result); } template<typename I, typename O> inline O move_impl(I f, I l, O result, ::boost::false_type) { while (f != l) { ::SimpleMove::iter_move(f, result); ++f; ++result; } return result; } } //Namespace for interface functions namespace SimpleMove { //move a value between two temporaries template <typename T> inline void move(T& from, T& to) { ::SimpleMove_Impl::move_impl(from, to); } //move an lvalue to a temporary template <typename T> inline T move(T& x) { T tmp; ::SimpleMove_Impl::move_impl(x,tmp); return tmp; } //convenience function to more between iterators template<typename T> inline void iter_move(T it1, T it2) { ::SimpleMove_Impl::move_impl(*it1, *it2); } //move algorithm - could also have move_backward similarly template<typename I, typename O> inline O move(I f, I l, O result) { //dispatches to std::copy for scalar return ::SimpleMove_Impl::move_impl(f, l, result, ::boost::is_scalar<std::iterator_traits<I>::value_type>::type()); } //overload for built-in array template<class T, std::size_t N> inline void move(T (& left)[N], T (& right)[N]) { ::SimpleMove::move(left, (left + N), right); } } //utility to quickly define move functions for user-defined type in //terms of swap #define MAKE_MOVEABLE_USING_SWAP(X) \ inline void move(X& from)\ {\ this->swap(from);\ }\ inline friend void move(X& from, X& to)\ {\ to.move(from);\ } //MoveOverloads.h #include <vector> #include <string> #include <boost/array.hpp> //Add additional overloads you require, for e.g., types in std:: or boost::, to //Overloads namespace in the examples of std:: containers below, //implemented in terms of swap namespace Overloads { template<typename T> inline void move(std::vector<T>& left, std::vector<T>& right) { left.swap(right); } inline void move(std::string& left, std::string& right) { left.swap(right); } template<typename T, size_t N> inline void move(boost::array<T,N>& left, boost::array<T,N>& right) { SimpleMove::move(left.begin(), left.end(), right.begin()); } } This could be useful as it provides a support for basic move semantics via a very small library, enabling 'moving' of your own types via custom move functions and e.g., of C++03 std:: containers via their swap functions, in conjunction with (N)RVO and copy-elision. Tom.