9 Jun 2012 22:21
rvalue ref best practices?
Daniel Larimer <dlarimer <at> gmail.com>
2012-06-09 20:21:41 GMT
2012-06-09 20:21:41 GMT
I am trying to define my library API and have been using rvalue references and have observed some patterns
that I would like to run by the community.
In c++03 I would pass almost everything by const& unless it was to be modified. This results in extra copies
any time a temporary is passed.
In c++11 I would pass anything that I plan on 'storing' or 'consuming' by &&. This results in the most
efficient code, but 'breaks' lvalue compatibility and forces me to consider having two (or more)
versions of every method. Unfortunately, you get a explosion of permutations of rvalue and const& as the
number of arguments increases and it makes getting function pointers much more 'ugly'.
Instead of providing two (or more) overloads, one for lvalues and one for rvalue, I have decided to only
provide the rvalue overload and if the user wants to pass an lvalue they wrap with std::ref(),
std::cref(), std::move() or my own helper copy() which makes the copy explicit and converts the lvalue to
a rvalue. (std::ref() and std::cref() could only work if the arguments are template parameters... in
which case the method would probably use std::forward<>() to handle the auto-detection of move vs copy
semantics.
template<typename T>
T copy( const T& v ) { return v; }
If I am writing a method that does not 'store' or 'consume' the input then I use const&
As rvalue references are quite new, this 'convention' may be unexpected by users. How would you feel about
using an API that assumed move semantics unless you explicitly specify copy or reference semantics? Is
there any reason that there shouldn't be a boost::copy() in boost/utility for use in this situation?
Are there some down sides to this approach that I am missing? In my experience 90% of functions that take
std::string() end up using a temporary and 90% of strings are 'calculated', 'used', and 'forgotten', yet
most methods take const& and result in a temporary value.
I should probably clarify, that I would use const& for any types that do not benefit from move semantics (no
deep copies). But when you are writing template code, you do not want to make that assumption about the type.
Another practice I am considering is making certain classes 'explicit copy only'.
template<typename T>
T copy( const T& t ) { return t; }
class test {
public:
test(){...};
test( test&& t ){...}
private:
template<typename T>
friend T copy(const T&t);
test& operator=(const test&); // not implemented
test( const test& t ){...}
std::vector<char> large_data;
};
int main( int argc, char** argv ) {
test x;
test y = copy(x);
test z = x; // error, test(const test&) is private...
return 0;
}
I would use this for any class that contains any 'movable members', such a strings, vectors, maps, etc.
Technically there is no reason why I couldn't copy them, but why shouldn't I attempt move semantics
everywhere possible unless I really do want to copy? With implicit copies there is no way for the compiler
to warn me that I am doing something potentially expensive.
If it didn't break backward compatibility I would suggest that all of the standard containers adopt an
explicit copy or move syntax or at least have a compiler flag to 'enable/disable' implicit copying for
'testing purposes'. Perhaps we could put them in a different namespace for those of us that want to disable
implicit copying of deep data types, ie: xc::vector<>, xc::string, etc.
Are these sane conventions? What could go wrong? I am I abusing rvalue references?
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
RSS Feed