
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.
<snip>
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) { //efficient swap member }
friend void swap(MyClass& lhs, MyClass& rhs){}
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 - it requires more boilerplate to be written, though there may be way(s) to automate the generation of the the user-type's move function. Also, with the library move functions implemented like this, you could still write algorithms like move()/move_backwards() (and helper functions such as iter_move() to move the values between two iterators) generically.
Also generic overloads of the move() function would be provided for built in arrays and e.g., boost::array //pseudocode void move(boost::array<T,N>& from, boost::array<T,N>& to){} void move(T (& left)[N], T (& right)[N]){} which would be implemented with a call to move(left[i], right[i]) for each element, in which case the elements would be swapped if a move overload has been defined for the element type, copied otherwise, thereby preserving optimality. It would work for multi-dimensional (boost) arrays in a recursive fashion.
So I guess the new question is, would this even smaller library be of any interest? Its use-cases would be the same as described in my original post, just to reiterate:
//use-case: returning a argument directly from a function
std::string foo(std::string s) { //e.g., append something to s //...
//compiler can perform rvo (compiler would likely not //perform nrvo if just used 'return s;')
return move(s); }
//use-case: moving a value to a function/ctor, when the value is not //required by the calling code after the function/ctor call
std::string s("hello"); void bar(std::string s){...}
bar(s); //copy bar(move(s)); //move //s has now been moved out of
//use-case: moving a value into place void MyClass::MyClass(std::string s) { //move value from s into default constructed member s_ move(s, s_); }
Note that the combination of both moving a value to a function/ctor and then moving it into place, e.g.,
std::string lv; //...
MyClass mc(move(lv));
effectively results in a zero-copy invocation, with just a small number of fixed size swap, default ctor and shallow dtor calls, so this is pretty efficient.
//use-case: moving a temporary into an lvalue std:: string lv; move(foo(), lv);
- Jeff Tom
Tom