
Jeffrey Lee Hellrung, Jr. wrote
On Wed, Jan 9, 2013 at 3:35 AM, THOMAS JORDAN <tomjordan766@gmail.com>wrote:
Apologies, my previous email I sent incomplete by mistake, please ignore that in favour of the following: Hi, Would there be interest for Boost in a small library providing a simple, non-instrusive emulation of basic move semantics for C++03 (though one which could also be used without harm in a C++11 environment)?
The library currently consists of a small number of 'move' function overloads, plus a trait class. It can be used to facilitate move semantics/efficient value-orientated software design in environments which lack, or forbid C++11 or other, more intrusive C++03-aware move libraries (e.g., old/buggy compiler, learning curve prohibitive, etc.). In conjunction it facilitates/forces copy-elision and return-value (RVO) optimisations in certain situations where they might otherwise not be feasible.
It works as follows: it detects at compile-time if the type of the argument(s) has a member swap function, and if so, it performs a swap, otherwise it calls the regular assignment operator ('move if possible, copy otherwise').
It can be used to provide an emulation of: - move-assignment between lvalues - moving an rvalue to an lvalue - moving an lvalue to a temporary It also supports moving between built-in arrays.
Example use-cases are (ignoring namespace):
#include "non_intrusive_move.h"
//returning a value
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); }
//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
//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)); //lv no longer needed
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.
//moving a temporary into an lvalue std:: string lv; move(foo(), lv); //nicer than foo().swap(lv)
The moved-from value is left in a 'valid' but undefined state, i.e., it can be destructed, assigned/moved to, swapped, etc.
The library is non-intrusive and generic, it will perform an efficient move in terms of swap for any type providing a member swap operation, e.g, std::vector, boost::array, user-defined types etc.
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 - 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. 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