
On 5/14/2010 1:19 AM, Ion Gaztañaga wrote:
On 12/05/2010 17:37, Jeffrey Lee Hellrung, Jr. wrote:
My intention is not to exclude this aim, I only consider it secondary to defining an agreed-upon protocol for data structures and algorithms to interoperate in a move-aware fashion within c++03.
Ok, perfect, so what do you think that protocol should look like? Just trying to get which requirements would be essential for different reviewers and try to merge them.
It seems, to me, like the requirements for a type T to be move constructable are: - T is implicitly convertible to rv<T>& (this is a non-const conversion, and the rv<T>& references the same object as the "from" object) - T has a constructor which can accept a rv<T>&; this constructor is the move constructor, which has move semantics. Refining move constructable would be the concept of move assignable: - T has an assignment operator accepting a rv<T>&; this assignment operator is the move assignment operator, which has move semantics. Some additional requirements that I'm not sure how to categorize, but may be desirable, are: - T has an assignment operator that moves from rvalues (assuming RVO; thus, this could be T::operator=(T) or T::operator=(rv<T>&)). - const T is explicitly convertible to rv<T> const& (*). Requirement (*) allows one to overload functions to capture rvalues of type T: void f(T& x) { ...copy x... } void f(rv<T> const& x) { ...copy x... } void f(rv<T>& x) { ...move x... } However, this prevents one from passing an object implicitly convertible to T, so I think a better recommendation for such overloading is something like template< class U > typename enable_if< is_convertible< U&, const T& > >::type f(U& x) { ...copy static_cast< const T& >(x)... } template< class U > typename disable_if< is_same< U, T > >::type f(const U& x) { ...copy static_cast< const T& >(x)... } void f(rv<T>& x) { ...move x... } Even though this is more complicated, it is more consistent with what one would get in C++0x, where implicit conversions to T when calling f would be allowed: void f(const T& x) { ...copy x... } void f(T&& x) { ...move x... } So, to be precise, I'm not sure that (*) should necessarily be encouraged. What do you think of that list so far?
Precisely why the requirements of a movable type should be explicitly defined, independent of how the move emulation is superficially effected in the code (i.e., which macro you use). As long as T is efficiently constructable and assignable from rvalues and (emulated) rvalue references (rv<T>& or T&&), the algorithm shouldn't care how the move emulation is actually effected within T.
Yes, algorithms should just need that, and a few functions (move(), forward()) to do their work. They shouldn't care about macros or implementation details.
I would just like some confidence in knowing what it means to be "compatible". As it stands now, today we'll use rv<T>& to emulate an rvalue reference, and tomorrow you might change the code to use sw<T>& to emulate an rvalue reference, or something even more radical. Who knows? There's (currently) nothing in the interface of (proposed) Boost.Move to suggest that the emulation machinery is nothing more than an implementation detail, which is problematic if code tries to enable move emulation in some alternative way, e.g., conditionally.
Ok. I don't know if someone will find a better emulation implementation. If we believe that we know enough C++03 to say rv<T> is the beste possible approach, there is no problem in pushing this outside the implementation detail. But we are ties with this implementation forever, and we should be aware of that limitation.
Of course; that's the unfortunate consequence of exposing rv<T> as part of the interface of (proposed) Boost.Move :( The emulation is very good, and I think its shortcomings are impossible to rectify in C++03. One thing that Boost.Move cannot do, and which I think is impossible in C++03, is capture rvalues as templated (emulated) rvalue references: template< class T > void f(rv<T>& x) will only capture explicitly created rvalue references. This makes forwarding more complicated and more restrictive. I can't think of any other "hard" restrictions on Boost.Move, though, i.e., things you can do in C++0x with rvalue references that you can't do with Boost.Move with sufficient machinery. Can you think of anything else? Perhaps we should consider a survey of past and present move emulation strategies to see how this one stacks up against it. This might also be prudent to include in the documentation. I know that Adobe has a move library that I studied some time ago; do you know of any others? - Jeff