Boost logo

Boost :

Subject: Re: [boost] [Review] Formal Review: Boost.Move - purpose + move enabling macros
From: Jeffrey Lee Hellrung, Jr. (jhellrung_at_[hidden])
Date: 2010-05-15 02:37:30


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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk